一道不错的题
虽然不知道为什么一群 D a l a o Dalao Dalao要用广义 S a m Sam Sam
S a m Sam Sam随便搞啊
首先我们可以先对一个串建 S a m Sam Sam
用另一个串在上面跑
我们考虑对于第二个串的每一个结尾对答案的贡献数
显然是
P
a
r
e
n
t
−
T
r
e
e
Parent-Tree
Parent−Tree上当前
S
a
m
Sam
Sam所在的点到的根的所有子串的大小
当然这只是大概的一个说法,实际上不准确,应该是到根的所有点集合内的子串的个数(包括不同地方的不同串每个都算一个)之和,感性理解到意思就可以了
现在考虑如何维护这样一个东西
显然我们可以先计算出每个 e n d p o s endpos endpos集合 e n d p o s endpos endpos大小
然后乘以这个集合内有的串的个数,也就是 l e n [ u ] − l e n [ l i n k [ u ] ] len[u]-len[link[u]] len[u]−len[link[u]]
因为我们也不可能一直向根跑来统计答案
所以我们利用前缀和的思想,把计算出的值往下传
这样最后每个点记录的就都是其到根的值了
但是注意第二个串匹配的时候不能直接加上当前这个点的值
应该是他 l i n k link link的值加上在这个集合的子串大小乘上 e n d p o s endpos endpos集合
所以记录一下当前最长匹配的串长就可以了
具体看代码更好理解一些
#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;
}