BZOJ4566: [Haoi2016]找相同字符

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。

 

Input

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

 

Output

输出一个整数表示答案

 

Sample Input

aabb
bbaa

Sample Output

10
 
裸题,用SAM来做的话先建出第一个串的后缀自动机,把第二个串拿上去运行,不完整节点直接计算,完整节点打个懒标记最后扫一遍就行了。
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=400010;
int to[maxn][26],l[maxn],fa[maxn],last=1,cnt=1;
int f[maxn],siz[maxn],od[maxn],c[maxn];
void extend(int c) {
	int p,q,np,nq;p=last;l[last=np=++cnt]=l[p]+1;siz[np]=1;
	for(;!to[p][c];p=fa[p]) to[p][c]=np;
	if(!p) fa[np]=1;
	else {
		q=to[p][c];
		if(l[p]+1==l[q]) fa[np]=q;
		else {
			l[nq=++cnt]=l[p]+1;
			fa[nq]=fa[q];fa[q]=fa[np]=nq;
			memcpy(to[nq],to[q],sizeof(to[nq]));
			for(;to[p][c]==q;p=fa[p]) to[p][c]=nq;
		}
	}
}
char s[maxn];
int main() {
	scanf("%s",s);
	for(int i=0;s[i];i++) extend(s[i]-'a');
	scanf("%s",s);
	int p=1,len=0;ll ans=0;
	rep(i,1,cnt) c[l[i]]++;
	rep(i,1,cnt) c[i]+=c[i-1];
	dwn(i,cnt,1) od[c[l[i]]--]=i;
	dwn(i,cnt,1) siz[fa[od[i]]]+=siz[od[i]];
	for(int i=0;s[i];i++) {
		int c=s[i]-'a';
		if(to[p][c]) len++,p=to[p][c];
		else {
			while(p&&!to[p][c]) p=fa[p];
			if(p) len=l[p]+1,p=to[p][c];
			else p=1,len=0;
		}
		ans+=(ll)siz[p]*(len-l[fa[p]]);f[fa[p]]++;
	}
	dwn(i,cnt,1) {
		int x=od[i];f[fa[x]]+=f[x];
		ans+=(ll)f[x]*siz[x]*(l[x]-l[fa[x]]);
	}
	printf("%lld\n",ans);
	return 0;
}

  

posted @ 2016-06-01 16:39  wzj_is_a_juruo  阅读(294)  评论(0编辑  收藏  举报