bzoj 3277: 串 & bzoj 3473: 字符串【后缀自动机||后缀数组】

建一个广义后缀自动机(每加完一个串都返回root),在parent树上dpsum记录合法长度,打着时间戳往上跳,最后每个串在自动机上跑一变统计答案即可。
后缀数组理解起来可能方便一点,但是难写,就只说一下思路……把这些串加上特殊字符拼起来,然后按着sa扫,对每个位置二分长度,再左右端点(用height判断是否有k个)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=300005;
int n,k;
long long ans;
string s[N];
struct sam
{
    int a[N][27],fa[N],len[N],v[N],c[N];
    long long sum[N];
    int p,q,np,nq,la,cnt;
    sam()   
    {
        la=++cnt;
    }
    void build(int c)
    {
        p=la;
        np=la=++cnt;
        len[np]=len[p]+1;
        while(!a[p][c]&&p) 
            a[p][c]=np,p=fa[p];
        if(!p) 
            fa[np]=1;
        else
        {
            q=a[p][c];
            if(len[q]==len[p]+1)   
                fa[np]=q;
            else
            {
                nq=++cnt;len[nq]=len[p]+1;
                memcpy(a[nq],a[q],sizeof(a[q]));
                fa[nq]=fa[q];
                fa[q]=fa[np]=nq;
                while(a[p][c]==q)  
                    a[p][c]=nq,p=fa[p];
            }
        }
    }
    void clc(int x)
    {
        if(x==1||v[x])   
            return;
        v[x]=1;  
        clc(fa[x]); 
        sum[x]+=sum[fa[x]];
    }
}sam;
int main()
{
    // freopen("string.in","r",stdin);
    // freopen("string.out","w",stdout);
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        for(int j=0;j<s[i].length();j++)   
            sam.build(s[i][j]-'a');  
        sam.la=1;
    }
    for(int i=1;i<=n;i++)
    {
        int x=1,now=i;
        for(int j=0;j<s[i].length();j++)
        {
            x=sam.a[x][s[i][j]-'a'];    
            int t=x;
            while(t&&sam.v[t]!=now)  
                sam.v[t]=now,sam.c[t]++,t=sam.fa[t];
        }
    }
    for(int i=1;i<=sam.cnt;i++)   
        sam.v[i]=0;
    for(int i=1;i<=sam.cnt;i++)    
        sam.sum[i]=(sam.c[i]>=k)*(sam.len[i]-sam.len[sam.fa[i]]);
    for(int i=1;i<=sam.cnt;i++)   
        sam.clc(i);
    for(int i=1;i<=n;i++)
    {
        int x=1;ans=0;
        for(int j=0;j<s[i].length();j++)   
            x=sam.a[x][s[i][j]-'a'],ans+=sam.sum[x];
        cout<<ans<<endl;;
    }
    return 0;
}
posted @ 2018-09-24 16:34  lokiii  阅读(199)  评论(0编辑  收藏  举报