bzoj 4566: [Haoi2016]找相同字符(后缀自动机)

4566: [Haoi2016]找相同字符

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 208   Solved: 125
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

输出一个整数表示答案

Sample Input

aabb
bbaa

Sample Output

10

HINT

Source

[ Submit][ Status][ Discuss]


题解:后缀自动机

对第一个串建立后缀自动机,然后用第二个串在自动机上进行匹配。

需要预处理出|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);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值