【HNOI2016】—找相同字符(后缀自动机)

传送门

一道不错的题

虽然不知道为什么一群DalaoDalao要用广义SamSam

SamSam随便搞啊

首先我们可以先对一个串建SamSam

用另一个串在上面跑

我们考虑对于第二个串的每一个结尾对答案的贡献数

显然是ParentTreeParent-Tree上当前SamSam所在的点到的根的所有子串的大小
当然这只是大概的一个说法,实际上不准确,应该是到根的所有点集合内的子串的个数(包括不同地方的不同串每个都算一个)之和,感性理解到意思就可以了

现在考虑如何维护这样一个东西

显然我们可以先计算出每个endposendpos集合endposendpos大小

然后乘以这个集合内有的串的个数,也就是len[u]len[link[u]]len[u]-len[link[u]]

因为我们也不可能一直向根跑来统计答案

所以我们利用前缀和的思想,把计算出的值往下传

这样最后每个点记录的就都是其到根的值了

但是注意第二个串匹配的时候不能直接加上当前这个点的值

应该是他linklink的值加上在这个集合的子串大小乘上endposendpos集合

所以记录一下当前最长匹配的串长就可以了

具体看代码更好理解一些

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
inline int read(){
    char ch=getchar();
    int res=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
}
const int N=400005;
int nxt[N][27],len[N],link[N],siz[N],A[N],p[N],ksiz[N],tot,last;
char a[N];
ll ans;
inline void sa_extend(int c){
    int cur=++tot,p=last;last=tot;
    len[cur]=len[p]+1,siz[cur]=1;
    for(;p&&!nxt[p][c];p=link[p])nxt[p][c]=cur;
    if(!p)link[cur]=1;
    else{
        int q=nxt[p][c];
        if(len[q]==len[p]+1)link[cur]=q;
        else{
            int clo=++tot;
            memcpy(nxt[clo],nxt[q],sizeof(nxt[q]));
            link[clo]=link[q],len[clo]=len[p]+1;
            for(;p&&nxt[p][c]==q;p=link[p])nxt[p][c]=clo;
            link[cur]=link[q]=clo;
        }
    }
}
int main(){
    scanf("%s",a+1);last=tot=1;
    int lent=strlen(a+1);
    for(int i=1;i<=lent;++i)sa_extend(a[i]-'a');
    for(int i=1;i<=tot;i++)A[len[i]]++;
    for(int i=1;i<=tot;i++)A[i]+=A[i-1];
    for(int i=1;i<=tot;i++)p[A[len[i]]--]=i;
    for(int i=tot;i;--i)siz[link[p[i]]]+=siz[p[i]];
    for(int i=1;i<=tot;i++)
        ksiz[p[i]]=ksiz[link[p[i]]]+(len[p[i]]-len[link[p[i]]])*siz[p[i]];
    scanf("%s",a+1);
    lent=strlen(a+1);
    int p=1,let=0;
    for(int i=1;i<=lent;i++){
        int c=a[i]-'a';
        for(;p&&!nxt[p][c];p=link[p]);
        if(!p)let=0,p=1;
        else{
            let=min(len[p],let)+1,p=nxt[p][c];
            ans+=ksiz[link[p]]+(let-len[link[p]])*siz[p];
        }
    }
    cout<<ans;
}

原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366373.html