zoukankan      html  css  js  c++  java
  • BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接:

    [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
    ",ans);
        }
    }
  • 相关阅读:
    摄影初识之一
    Photoshop CS6的安装
    chcon可实现对文件的SEAndroid安全标签的修改
    ubuntu启动失败the system is running in low graphics mode
    将ubuntu14.04设置为文本模式启动?
    Android数据库之SQLite数据库
    系统崩溃分析
    Oops 的栈信息分析
    android framework 之JNI
    SecureCRT语法高亮设置
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10298945.html
Copyright © 2011-2022 走看看