4566: [Haoi2016]找相同字符
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 208 Solved: 125
[ Submit][ Status][ Discuss]
Description
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。
Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaa
bbaa
Sample Output
10
HINT
Source
题解:后缀自动机
对第一个串建立后缀自动机,然后用第二个串在自动机上进行匹配。
需要预处理出|right|和sum
sum表示的是当前状态之前fa链上点的总贡献。因为如果匹配到当前的状态,那么fa链上之前的就都被匹配上了。
sum[i]=sum[fa[i]]+(l[fa[i]]-l[fa[fa[i]]])*right[fa[i]]
进行匹配的时候匹配到一个节点的贡献为sum[p]+(tmp-l[fa[p]])*right[p]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 400003
#define LL long long
using namespace std;
int n,m,cnt,np,nq,q,p,last,root;
int ch[N][30],fa[N],l[N],r[N],pos[N],v[N],cl[N],mark[N];
LL sum[N],ans;
char s[N],s1[N];
void extend(int x)
{
int c=s[x]-'a';
p=last; np=++cnt; last=np;
l[np]=x;
for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if (!p) fa[np]=root;
else {
q=ch[p][c];
if (l[q]==l[p]+1) fa[np]=q;
else {
nq=++cnt; l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[nq]);
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void solve()
{
int tmp=0; p=1;
for (int i=1;i<=m;i++) {
int c=s1[i]-'a';
if (ch[p][c]) p=ch[p][c],tmp++;
else {
while (p&&!ch[p][c]) p=fa[p];
if (!p) p=root,tmp=0;
else tmp=l[p]+1,p=ch[p][c];
}
ans+=sum[p]+(LL)(tmp-l[fa[p]])*(LL)r[p];
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
last=root=++cnt;
for (int i=1;i<=n;i++)
extend(i);
for (int i=1;i<=cnt;i++) v[l[i]]++;
for (int i=1;i<=n;i++) v[i]+=v[i-1];
for (int i=1;i<=cnt;i++) pos[v[l[i]]--]=i;
p=1;
for (int i=1;i<=n;i++) {
p=ch[p][s[i]-'a']; r[p]++;
}
for (int i=cnt;i;i--) {
int t=pos[i];
r[fa[t]]+=r[t];
}
for (int i=1;i<=cnt;i++) {
int t=pos[i];
sum[t]=sum[fa[t]]+(LL)(l[fa[t]]-l[fa[fa[t]]])*(LL)r[fa[t]];
}
scanf("%s",s1+1);
m=strlen(s1+1);
solve();
printf("%I64d\n",ans);
}