bzoj4566【HAOI2016】找相同字符

4566: [Haoi2016]找相同字符

Time Limit: 20 Sec  Memory Limit: 256 MB

Submit: 128  Solved: 75

[Submit][Status][Discuss]

Description

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

个子串中有一个位置不同。

Input

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

Output

输出一个整数表示答案

Sample Input

aabb

bbaa

Sample Output

10

广义后缀自动机

建立两个串的后缀自动机,统计一下每个节点的两个串出现次数sz[i][0]和sz[i][1],则答案等于∑(mx[i]-mx[fa[i]])*sz[i][0]*sz[i][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=-1;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-1];
//	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;
}
时间: 2024-12-13 19:43:48

bzoj4566【HAOI2016】找相同字符的相关文章

BZOJ4566:[Haoi2016]找相同字符

4566: [Haoi2016]找相同字符 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 545  Solved: 302[Submit][Status][Discuss] Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. Input 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母

BZOJ4566 [Haoi2016]找相同字符 【后缀数组】

题目 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. 输入格式 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式 输出一个整数表示答案 输入样例 aabb bbaa 输出样例 10 题解 先考虑暴力怎么做 我们枚举两个串的各自一个后缀suffix(i)和suffix(j) 则他们对答案的贡献是LCP(suffix(i),suffix(j)) 如

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

[BZOJ4566]找相同字符(后缀自动机) 题面 BZOJ 题解 看到多串处理,\(SA\)就连起来 \(SAM???\) 单串建自动机 然后其他串匹配 对于一个串建完\(SAM\)后 另一个串在\(SAM\)上匹配 记录当前匹配的最大长度 匹配了当前位置的话,就能产生一定的贡献 但是很显然,沿着\(parent\)往上,所有点都能够产生贡献 所以匹配完再沿着\(parent\)做一遍类似\(dp\)的东西算贡献 #include<iostream> #include<cstdio&g

【BZOJ4566】找相同字符(后缀数组)

[BZOJ4566]找相同字符(后缀数组) 题面 BZOJ 题解 后缀数组的做法,应该不是很难想 首先看到两个不同的串,当然是接在一起求\(SA,height\) 那么,考虑一下暴力 在两个串各枚举一个后缀,他们的\(lcp\)就是对答案产生的贡献 现在优化一下,按照\(SA\)的顺序枚举来处理\(lcp\) 利用一个单调栈维护一下,每次记录一下前面有多少个的贡献和当前答案一样就好啦 只是有点难写... #include<iostream> #include<cstdio> #in

P3181 [HAOI2016]找相同字符

P3181 [HAOI2016]找相同字符 对一个串建SAM,另一个串在这上面跑,到达一点时,假设经过了\(cnt\)个点 计算这个串所有后缀产生的贡献就好了,直接暴力跑上去可能会超时,topsort预处理一下 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #incl

[HAOI2016]找相同字符(后缀数组+单调栈)

[HAOI2016]找相同字符(后缀数组+单调栈) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 我们把两个字符串接在一起,中间加一个分隔符.如\(\text{AABB}\)和\(\text{BBAA}\)变成\(\text{AABB|BBAA}\).我们考虑两个相同字串,如\(\text{BB}\),它在新串中对应了两个后缀\(BB|BBAA\)和\(\text{BBAA}\)的LCP. 容易发现,LC

[HAOI2016]找相同字符(广义SAM)

[HAOI2016]找相同字符(广义SAM) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 此题有一个比较繁琐的后缀数组做法,但是用广义SAM可以秒杀. 把两个串建成广义SAM,对于每个后缀,记录\(endpos\)集合中落在第一个串中和第二个串中的位置个数,记为\(cnt_{x,0},cnt_{x,1}\). 对于自动机上的每个节点\(x\),出现位置方案数的贡献是\(cnt_{x,0} \cdot c

BZOJ4566:[HAOI2016]找相同字符——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4566 https://www.luogu.org/problemnew/show/P3181 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 广义后缀自动机,两个串各处理他们的size(或right?),然后对结点l排序,对于每个结点他们的size相乘即为答案. ……等等怎么WA了啊. 比如: aba abaa 这组数

4566: [Haoi2016]找相同字符 SAM

折腾了好久.不过收获还是很多的.第一次自己去画SAM所建出来fail树.深入体会了这棵树的神奇性质. 当然,我最终靠着自己A掉了.(这是我第一次推SAM的性质(以前都是抄别人的,感觉自己好可耻),不过感觉好像是摸着黑行走啊!) 这道题,可以先对第一个串建出后缀自动机.然后第二个串在后缀自动机上跑. 首先,SAM所建出的fail树的性质有: 1: 树上一个节点对应了多个串,串的个数是 len[x] - len[fa[x]], 同时它们都出现了 sz[x] 次(感觉好像说不大清,可以看一下代码对于s

●BZOJ 4566 [Haoi2016]找相同字符

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4566 题解: 后缀数组,单调栈.把两个串A,B拼接起来,中间用没出现过的字符隔开.然后用倍增算法求出 sa[] rank[] height[]接着用单调栈维护出两个数组 L[],R[],意义如下:L[i]:表示在后缀数组中,排名最小(记其排名为 L[i])的后缀与排名为 i的后缀的LCP>=hei[i]同理 R[i]:表示在后缀数组中,排名最大(记其排名为 R[i])的后缀与排名为 i的后