BZOJ4566 HAOI2016找相同字符(后缀自动机)

  对第一个串建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\n";
#else
	const char LL[]="%lld\n";
#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;
}

  

posted @ 2019-05-04 01:06  Gloid  阅读(150)  评论(0编辑  收藏  举报