BZOJ传送门
洛谷传送门
解析:
这种题显然就是建立一个SAM,拿另一个上去跑。
考虑跑到某一个点的贡献。显然就是 ∣ r i g h t ∣ ∗ ( l e n − m n [ s ] + 1 ) |right|*(len-mn[s]+1) ∣right∣∗(len−mn[s]+1)
就是所有出现出现的次数*所有不同串长种类数。
显然这个是需要跳fail链统计的,我们维护一下每个位置被匹配的次数,最后从后向前统计一遍就行了
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
cs int N=200005;
namespace SAM{
int son[N<<1][26],fa[N<<1],len[N<<1],siz[N<<1],cnt[N<<1],f[N<<1];
int last=1,now=1,l;
inline void push_back(char c){
c-='a';++l;
int p=last,cur=++now;
len[cur]=len[last]+1;siz[cur]=1;
for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
if(!p)fa[cur]=1;
else if(len[son[p][c]]==len[p]+1)fa[cur]=son[p][c];
else {
int clone=++now,q=son[p][c];
memcpy(son[clone],son[q],sizeof son[q]);
len[clone]=len[p]+1;
fa[clone]=fa[q];
fa[q]=fa[cur]=clone;
for(;p&&son[p][c]==q;p=fa[p])son[p][c]=clone;
}
last=cur;
}
int a[N<<1],bin[N];
inline void radix_sort(){
for(int re i=1;i<=now;++i)++bin[len[i]];
for(int re i=1;i<=l;++i)bin[i]+=bin[i-1];
for(int re i=1;i<=now;++i)a[bin[len[i]]--]=i;
for(int re i=now;i;--i)siz[fa[a[i]]]+=siz[a[i]];
}
ll ans;
inline void calc(char *s,int l){
int u=1,nowlen=0;
for(int re i=0;i<l;++i){
if(son[u][s[i]-'a']){u=son[u][s[i]-'a'];++nowlen;}
else {
while(u&&!son[u][s[i]-'a'])u=fa[u];
if(!u)u=1,nowlen=0;
else nowlen=len[u]+1,u=son[u][s[i]-'a'];
}
ans+=(ll)(nowlen-len[fa[u]])*siz[u];++cnt[u];
}
for(int re i=now;i>1;--i){
int p=a[i];f[fa[p]]+=f[p]+cnt[p];
ans+=(ll)f[p]*siz[p]*(len[p]-len[fa[p]]);
}
cout<<ans<<"\n";
}
}
char s[N];int len;
signed main(){
scanf("%s",s);len=strlen(s);
for(int re i=0;i<len;++i)SAM::push_back(s[i]);
SAM::radix_sort();
scanf("%s",s);len=strlen(s);
SAM::calc(s,len);
return 0;
}