对第一个串建SAM,第二个串在上面跑,记录当前前缀匹配的最长后缀长度l,每次考虑当前前缀的贡献,对于当前所在节点显然是|right|*(l-len[fa]),而对于其parent树上所有祖先的贡献显然是|right|*(len-len[fa])。预处理一下就好了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define N 400010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,cnt=1,last=1,son[N][26],fail[N],len[N],id[N],size[N],tot[N]; ll ans,f[N],val[N]; char a[N],b[N]; void ins(int c) { int x=++cnt,p=last;last=x;len[x]=len[p]+1;size[x]=1; while (!son[p][c]) son[p][c]=x,p=fail[p]; if (!p) fail[x]=1; else { int q=son[p][c]; if (len[p]+1==len[q]) fail[x]=q; else { int y=++cnt; len[y]=len[p]+1; memcpy(son[y],son[q],sizeof(son[q])); fail[y]=fail[q],fail[q]=fail[x]=y; while (son[p][c]==q) son[p][c]=y,p=fail[p]; } } } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif scanf("%s",a+1);n=strlen(a+1); scanf("%s",b+1);m=strlen(b+1); for (int i=1;i<=n;i++) ins(a[i]-'a'); for (int i=1;i<=cnt;i++) tot[len[i]]++; for (int i=1;i<=n;i++) tot[i]+=tot[i-1]; for (int i=1;i<=cnt;i++) id[tot[len[i]]--]=i; for (int i=cnt;i>=1;i--) size[fail[id[i]]]+=size[id[i]]; for (int i=1;i<=cnt;i++) val[id[i]]=val[fail[id[i]]]+1ll*size[id[i]]*(len[id[i]]-len[fail[id[i]]]); int k=1,l=0; for (int i=1;i<=m;i++) { while (!son[k][b[i]-'a']&&k) k=fail[k],l=len[k]; if (!k) k=1,l=0; else l++,k=son[k][b[i]-'a']; ans+=val[fail[k]]+1ll*size[k]*(l-len[fail[k]]); } cout<<ans; return 0; }