【BZOJ】5417: [Noi2018]你的名字 -后缀自动机&线段树可持久化合并

版权声明:转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/82181266

传送门:bzoj5417


题解

首先还是那句话,要清楚后缀自动机的本质(后缀自动机详解)。

S 建一个后缀自动机,每次对 T 同样建一个后缀自动机。

考虑 l = 1 , r = | S | 的情况:

l i m i 表示字符串 T 区间 [ 1 , i ] 所能匹配 S 的最长后缀 [ i l i m i + 1 , i ] ( l i m i > 0 ) (若 l i m i = 0 ,则 T i 字符没有在 S 中出现)。 l i m i 可以同步在 S 的后缀自动机不断向下扩展得到,当无法匹配时,不断跳 f a i l 链直到可以匹配上,保证匹配的是字符串 T [ 1 , i ] 的后缀。

假设对于 S 所构成的自动机,节点 i r i g h t 集合中所能表示的字符串最大长度为 m x i t a g i 表示该 r i g h t 集合字符串第一次出现的位置(这些字符串是后缀包含关系,末位置相同),节点 i f a i l 链指向节点 f l i

则: a n s = i = 2 c n t m a x ( 0 , m x i m a x ( m x f l i , l i m t a g i ) )

实际上我们只需要在 S 的后缀自动机上移动,最后 O ( c n t ) 枚举一遍在 T 的后缀自动机上统计答案( c n t T 后缀自动机上节点个数)。
1 号节点为后缀自动机起始节点。

再考虑 l 1 , r | S | 的情况:

稍有区别的是,在处理 l i m i 时,需要判断当前的字符串是否在 S [ l , r ] 区间中出现,可以先对 S 的后缀自动机每个节点按 m x i 排序,以每个节点建立线段树表示出现位置,再不断向 f l i 合并。

每次在 S 的后缀自动机往下扩展时,判断 [ r t [ p o s ] , l + l e n , r ] 中是否出现即可( p o s 表示当前移动到的后缀自动机节点, l e n 表示当前匹配了 T [ i l e n + 1 , i ] ,询问 [ l + l e n , r ] 区间的出现位置才能保证字符串出现在 [ l , r ] 内)。
O ( | S | + l o g | S | + ( | T | + | T | l o g | S | ) )


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+100,M=2e7+100,inf=0x3f3f3f3f;
int que,n,m,rt[N],lim[N];
char S[N],T[N];ll ans;

namespace tree{
    #define mid (((l)+(r))>>1)
    #define lc ch[k][0]
    #define rc ch[k][1]
    int tot,ch[M][2];

    inline void ins(int &k,int l,int r,int pos)
    {
        if(!k) k=++tot;
        if(l==r) return;
        if(pos<=mid) ins(lc,l,mid,pos);
        else ins(rc,mid+1,r,pos);
    }

    inline int merge(int x,int y)
    {
        if(!x || !y) return x+y;
        int k=++tot;
        lc=merge(ch[x][0],ch[y][0]);
        rc=merge(ch[x][1],ch[y][1]);
        return k;
    }

    inline bool query(int k,int l,int r,int L,int R)
    {
        if(!k || L>R) return false;
        if(L<=l && r<=R) return true;
        if(L<=mid) if(query(lc,l,mid,L,R)) return true;
        if(R>mid) if(query(rc,mid+1,r,L,R)) return true;
        return false; 
    }

}

namespace SAM{
   int p,q,cnt=1,cur=1;
   int ch[N][26],mx[N],fl[N],cc[N],sa[N],in[N];

   inline void insert(int alp,int id)
   {
       p=cur;cur=++cnt;mx[cur]=id;in[cur]=1;
       for(;!ch[p][alp] && p;p=fl[p]) ch[p][alp]=cur;
       if(!p) fl[cur]=1;else{
          q=ch[p][alp];
          if(mx[q]==mx[p]+1) fl[cur]=q;else{
            mx[++cnt]=mx[p]+1;
            memcpy(ch[cnt],ch[q],sizeof(ch[q]));
            fl[cnt]=fl[q];fl[cur]=fl[q]=cnt;
            for(;ch[p][alp]==q;p=fl[p]) ch[p][alp]=cnt;
          }
       }
   }

   inline void build()
   {
       register int i,j;
       for(i=1;i<=n;++i) insert(S[i]-'a',i);
       for(i=1;i<=cnt;++i) cc[mx[i]]++;
       for(i=1;i<=n;++i) cc[i]+=cc[i-1];
       for(i=cnt;i>1;--i) sa[cc[mx[i]]--]=i;
       for(i=cnt;i>1;--i){
          j=sa[i];
          if(in[j]) tree::ins(rt[j],1,n,mx[j]);  
          rt[fl[j]]=tree::merge(rt[fl[j]],rt[j]);
       }
   }

}

namespace solve{
     int p,q,cur=1,cnt=1;
     int ch[N][26],mx[N],fl[N],tag[N];

     inline void clr()
     {
        register int i,j;
        for(i=0;i<=cnt;++i) memset(ch[i],0,sizeof(ch[i]));
        cur=cnt=1;
     }

     inline void ins(int alp,int id)
     {
         p=cur;cur=++cnt;mx[cur]=id;tag[cur]=id;
         for(;!ch[p][alp] && p;p=fl[p]) ch[p][alp]=cur;
         if(!p) fl[cur]=1;else{
            q=ch[p][alp];
            if(mx[q]==mx[p]+1) fl[cur]=q;else{
                mx[++cnt]=mx[p]+1;tag[cnt]=tag[q];
                memcpy(ch[cnt],ch[q],sizeof(ch[q]));
                fl[cnt]=fl[q];fl[q]=fl[cur]=cnt;
                for(;ch[p][alp]==q;p=fl[p]) ch[p][alp]=cnt;
            }
         }
     }

     inline ll work()
     {
        register int i,j,L,R,len,u,alp;
        scanf("%s%d%d",T+1,&L,&R);
        clr();
        m=strlen(T+1);
        for(len=0,u=1,i=1;i<=m;++i){
          alp=T[i]-'a';
          ins(alp,i);
          for(;;){
              if(SAM::ch[u][alp] && tree::query(rt[SAM::ch[u][alp]],1,n,L+len,R)){
                 len++;
                 u=SAM::ch[u][alp];
                 break;
              }
              if(len==0) break;
              len--;
              if(len==SAM::mx[SAM::fl[u]]) u=SAM::fl[u];
          }
          lim[i]=len;   
        }
        for(ans=0,i=2;i<=cnt;++i)
         ans+=max(0,mx[i]-max(mx[fl[i]],lim[tag[i]]));
        return ans;
    }
}

inline void out(ll x){if(x>9) out(x/10);putchar('0'+x%10);}

int main(){
    scanf("%s",S+1);
    n=strlen(S+1);
    SAM::build();
    scanf("%d",&que);
    for(;que;--que) out(solve::work()),puts("");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/82181266