4566: [Haoi2016]找相同字符
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 128 Solved: 75
[Submit][Status][Discuss]
Description
给定两个字符串,求出在两个字符串中各取出1个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有1个位置不同。
Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
Sample Input
aabb
bbaa
Sample Output
10
广义后缀自动机
建立两个串的后缀自动机,统计1下每一个节点的两个串出现次数sz[i][0]和sz[i][1],则答案等于∑(mx[i]-mx[fa[i]])*sz[i][0]*sz[i][1]。
另外1个小细节就是拓扑排序的地方要注意1下。
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define N 200005
#define M 800005
using namespace std;
int n,m,cnt=1,last;
int fa[M],mx[M],c[M][26],sz[M][2],v[N],q[M];
ll ans;
char a[N],b[N];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=⑴;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int d[M];
queue<int> qu;
void calc()
{
// F(i,1,cnt) v[mx[i]]++;
// F(i,1,max(n,m)) v[i]+=v[i⑴];
// F(i,1,cnt) q[v[mx[i]]--]=i; //这样也是对的
int tmp=cnt;
F(i,1,cnt) d[fa[i]]++;
F(i,1,cnt) if (!d[i]) qu.push(i);
while (!qu.empty())
{
int x=qu.front();qu.pop();
q[tmp--]=x;d[fa[x]]--;
if (!d[fa[x]]) qu.push(fa[x]);
}
D(i,cnt,1)
{
sz[fa[q[i]]][0]+=sz[q[i]][0];
sz[fa[q[i]]][1]+=sz[q[i]][1];
}
F(i,1,cnt) ans+=(ll)(mx[i]-mx[fa[i]])*sz[i][0]*sz[i][1];
}
void add(int x)
{
int p=last;
if (c[p][x]&&mx[c[p][x]]==mx[p]+1){last=c[p][x];return;}
int np=++cnt;last=np;
mx[np]=mx[p]+1;
while (p&&!c[p][x]) c[p][x]=np,p=fa[p];
if (!p) fa[np]=1;
else
{
int q=c[p][x];
if (mx[q]==mx[p]+1) fa[np]=q;
else
{
int nq=++cnt;
mx[nq]=mx[p]+1;
memcpy(c[nq],c[q],sizeof(c[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
while (p&&c[p][x]==q) c[p][x]=nq,p=fa[p];
}
}
}
int main()
{
scanf("%s",a+1);scanf("%s",b+1);
n=strlen(a+1);m=strlen(b+1);
last=1;F(i,1,n) add(a[i]-'a'),sz[last][0]++;
last=1;F(i,1,m) add(b[i]-'a'),sz[last][1]++;
calc();
cout<<ans<<endl;
return 0;
}