1. 程式人生 > >BZOJ5417[Noi2018]你的名字——後綴自動機+線段樹合並

BZOJ5417[Noi2018]你的名字——後綴自動機+線段樹合並

else while pre 所有 http class () pan 維護

題目鏈接:

[Noi2018]你的名字

題目大意:給出一個字符串$S$及$q$次詢問,每次詢問一個字符串$T$有多少本質不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$個字符到第$r$個字符組成的子串)。

首先考慮$l=1,r=|S|$的情況,對$T$串建立後綴自動機,可以知道$T$串本質不同的子串個數就是後綴自動機上每個點的$len[i]-len[pre[i]]$($len[i]$代表這個點所能表示的最長串長度),這也就是後綴自動機上每個點貢獻的子串個數。對於每個點它貢獻的串顯然是它的後綴,但這些後綴中會有一些是$S$串的子串,所以就需要減掉這部分的貢獻。我們再對$S$建立後綴自動機並將$T$串在$S$串上匹配求出$mx[i]$表示$T[1,i]$的最長後綴是$S$串的子串的長度。匹配時我們用$cnt$記錄匹配完$T$串每個字符$i$之後得到的$mx[i]$,顯然如果能往下走就$cnt++$,否則就跳父親節點直到能往下走為止(假設跳父親節點跳到$p$),然後將$cnt$置成$len[p]$再繼續往下走,這樣就能得到$T$串中每個點的$mx[i]$。對於$T$串的後綴自動機上每個點$i$再記錄它$endpos$集合中最小的位置$pos[i]$(因為$endpos$集合中任意位置的後綴都相同,所以隨便哪個都行,但第一個顯然在建後綴自動機時就能記錄)。那麽最後的答案就是$ans=\sum max(0,len[i]-max(mx[pos[i]],len[pre[i]]))$,其中$i$為後綴自動機上的所有節點。


現在考慮$l,r$任意的情況,顯然$mx[i]$要變成$T[1,i]$的最長後綴是$S[l,r]$的子串的最長長度。所以我們需要維護$S$串後綴自動機上每個節點的$endpos$集合,對於$S$串後綴自動機上每個節點動態開點建一棵線段樹存這個節點的$endpos$集合,然後在$pre$樹上從下往上線段樹合並即可維護出每個點的$endpos$集合。那麽當$T$串在$S$串後綴自動機上匹配時,我們需要知道接下來要往下走的節點的$endpos$集合中是否有在$[l+cnt,r]$之中的。如果有就往下走並$cnt++$,否則一點點減小$cnt$並繼續在線段樹上查找,當$cnt$減小到$len[pre[i]]$時說明當前點不能匹配$T$串的下一個字符,這時就要跳到$pre[i]$再重復上述操作,最後求答案的部分與$l=1,r=|S|$的情況相同。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll ans;
int n,q;
int L,R;
int tot;
char s[500010];
char t[500010];
int to[1000010];
int mx[500010];
int head[1000010];
int next[1000010];
int root[1000010];
void add(int x,int y)
{
    next[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
namespace segment_tree
{
    int cnt;
    int ls[36000010];
    int rs[36000010];
    void insert(int &rt,int l,int r,int k)
    {
        if(!rt)
        {
            rt=++cnt;
        }
        if(l==r)
        {
            return ;
        }
        int mid=(l+r)>>1;
        if(k<=mid)
        {
            insert(ls[rt],l,mid,k);
        }
        else
        {
            insert(rs[rt],mid+1,r,k);
        }
    }
    int merge(int x,int y)
    {
        if(!x||!y)
        {
            return x+y;
        }
        int rt=++cnt;
        ls[rt]=merge(ls[x],ls[y]);
        rs[rt]=merge(rs[x],rs[y]);
        return rt;
    }
    int query(int rt,int l,int r,int L,int R)
    {
        if(L>R)
        {   
            return 0;
        }
        if(!rt)
        {
            return 0;
        }
        if(L<=l&&r<=R)
        {
            return 1;
        }
        int mid=(l+r)>>1;
        int res=0;
        if(L<=mid)
        {
            res|=query(ls[rt],l,mid,L,R);
        }
        if(R>mid)
        {
            res|=query(rs[rt],mid+1,r,L,R);
        }
        return res;
    }
}
void dfs(int x)
{
    for(int i=head[x];i;i=next[i])
    {
        dfs(to[i]);
        root[x]=segment_tree::merge(root[x],root[to[i]]);
    }
}
namespace SAM_S
{
    int trs[1000010][26];
    int len[1000010];
    int pre[1000010];
    int cnt=1;
    int last=1;
    void insert(int x,int y)
    {
        int p=last;
        int np=++cnt;
        last=np;
        len[np]=len[p]+1;
        segment_tree::insert(root[np],1,n,y);
        for(;p&&!trs[p][x];p=pre[p])
        {
            trs[p][x]=np;
        }
        if(!p)
        {
            pre[np]=1;
        }
        else
        {
            int q=trs[p][x];
            if(len[q]==len[p]+1)
            {
                pre[np]=q;
            }
            else
            {
                int nq=++cnt;
                pre[nq]=pre[q];
                memcpy(trs[nq],trs[q],sizeof(trs[q]));
                pre[np]=pre[q]=nq;
                len[nq]=len[p]+1;
                for(;p&&trs[p][x]==q;p=pre[p])
                {
                    trs[p][x]=nq;
                }
            }
        }
    }
    void build()
    {
        for(int i=1;i<=n;i++)
        {
            insert(s[i]-‘a‘,i);
        }
        for(int i=2;i<=cnt;i++)
        {
            add(pre[i],i);
        }
        dfs(1);
    }
}
namespace SAM_T
{
    int trs[1000010][26];
    int len[1000010];
    int pre[1000010];
    int pos[1000010];
    int cnt;
    int last;
    void initial()
    {
        memset(trs,0,(cnt+2)*sizeof(trs[0]));
        cnt=last=1;
    }
    void insert(int x,int y)
    {
        int p=last;
        int np=++cnt;
        last=np;
        len[np]=len[p]+1;
        pos[np]=y;
        for(;p&&!trs[p][x];p=pre[p])
        {
            trs[p][x]=np;
        }
        if(!p)
        {
            pre[np]=1;
        }
        else
        {
            int q=trs[p][x];
            if(len[q]==len[p]+1)
            {
                pre[np]=q;
            }
            else
            {
                int nq=++cnt;
                pre[nq]=pre[q];
                pos[nq]=pos[q];
                memcpy(trs[nq],trs[q],sizeof(trs[q]));
                pre[np]=pre[q]=nq;
                len[nq]=len[p]+1;
                for(;p&&trs[p][x]==q;p=pre[p])
                {
                    trs[p][x]=nq;
                }
            }
        }
    }
    void build()
    {
        initial();
        int tlen=strlen(t+1);
        for(int i=1;i<=tlen;i++)
        {
            insert(t[i]-‘a‘,i);
        }
    }
}
void solve(int L,int R)
{
    ans=0;
    int tlen=strlen(t+1);
    int now=1;
    int res=0;
    for(int i=1;i<=tlen;i++)
    {
        while(1)
        {
            if(!segment_tree::query(root[SAM_S::trs[now][t[i]-‘a‘]],1,n,L+res,R))
            {
                if(!res)
                {
                    break;
                }
                res--;
                if(res==SAM_S::len[SAM_S::pre[now]])
                {
                    now=SAM_S::pre[now];
                }
            }
            else
            {
                res++;
                now=SAM_S::trs[now][t[i]-‘a‘];
                break;
            }
        }
        mx[i]=res;
    }
    for(int i=2;i<=SAM_T::cnt;i++)
    {
        ans+=max(0,SAM_T::len[i]-max(SAM_T::len[SAM_T::pre[i]],mx[SAM_T::pos[i]]));
    }
}
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    SAM_S::build();
    scanf("%d",&q);
    while(q--)
    {
        scanf("%s",t+1);
        scanf("%d%d",&L,&R);
        SAM_T::build();
        solve(L,R);
        printf("%lld\n",ans);
    }
}

BZOJ5417[Noi2018]你的名字——後綴自動機+線段樹合並