后缀自动机的应用

求不同子串个数

LUOGU P2408

解题思路

  这个其实就是在后缀自动机上统计不同的路径条数,可以\(dp\)解决,\(f[u]=\sum\limits_{v=son[u]} f[v]+1\),时间复杂度\(O(n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
const int N=200005;
typedef long long LL;

inline int rd(){
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}

int n,c[N],a[N],l[N],r[N],len;
LL f[N];
char s[N];

struct SAM{
    int ch[N][28],fa[N],cnt,lst,l[N];
    void insert(int c){
        int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
        for(;!ch[p][c] && p;p=fa[p]) ch[p][c]=np;
        if(!p) fa[np]=1;
        else {
            int q=ch[p][c]; 
            if(l[q]==l[p]+1) fa[np]=q;
            else {
                int nq=++cnt; l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[nq]));
                fa[nq]=fa[q]; fa[np]=fa[q]=nq;
                for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
            } 
        }
    }
    void build(){
        for(int i=1;i<=n;i++) insert(s[i]-'a'+1);
        for(int i=1;i<=cnt;i++) c[l[i]]++;
        for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
        for(int i=1;i<=cnt;i++) a[c[l[i]]--]=i;
    }
    void solve(){
        for(int i=cnt;i;i--)
            for(int j=1;j<=26;j++)
                if(ch[a[i]][j]) f[a[i]]+=f[ch[a[i]][j]]+1;
        printf("%lld\n",f[1]);
    }
}sam;

int main(){
    sam.lst=sam.cnt=1; n=rd();
    scanf("%s",s+1); sam.build();
    sam.solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sdfzsyq/p/10446178.html