BZOJ4566:[Haoi2016]找相同字符

4566: [Haoi2016]找相同字符

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 545  Solved: 302
[Submit][Status][Discuss]

Description

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

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

Input

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

Output

输出一个整数表示答案

Sample Input

aabb

bbaa

Sample Output

10

思路{

  后缀数组套路题。

  LCP长度大的能对长度小的做出贡献。

  那把两个串连成一个。sa[i],sa[i-1],height[i]分组,用并查集维护集合在1串和二串中的个数,乘法原理统计答案。

}

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<30)
#define il inline
#define RG register
#define LL long long
#define maxx 200010*2
using namespace std;
char s[maxx],t[maxx];int tong[maxx];
int len1,len2,X[maxx],Y[maxx],rnk[maxx],sa[maxx],height[maxx],LEN;LL A;
il bool comp(int *r,int i,int j,int len){return r[i+len]==r[j+len]&&r[i]==r[j];}
il void build_sa(int n){
	int *x=X,*y=Y,*t,Max=88988;
	for(int i=0;i<n;++i)tong[x[i]=s[i]]++;
	for(int i=1;i<Max;++i)tong[i]+=tong[i-1];
	for(int i=n-1;i!=-1;i--)sa[--tong[x[i]]]=i;
	for(int j=1,p=0,i;p<n;j<<=1,Max=p){
		for(i=n-1,p=0;i>=n-j;--i)y[p++]=i;
		for(i=0;i<n;++i)if(sa[i]>=j)y[p++]=sa[i]-j;
		memset(tong,0,sizeof(tong));
		for(i=0;i<n;++i)tong[x[y[i]]]++;
		for(i=1;i<=Max;++i)tong[i]+=tong[i-1];
		for(i=n-1;i!=-1;i--)sa[--tong[x[y[i]]]]=y[i];
		for(t=x,x=y,y=t,i=1,p=1,x[sa[0]]=0;i<n;++i)
			x[sa[i]]=comp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}
il void geth(){
	int i,j,k=0;
	for(i=1;i<=LEN;++i)rnk[sa[i]]=i;
	for(i=0;i<LEN;height[rnk[i++]]=k)
		for((k?k--:0),j=sa[rnk[i]-1];s[j+k]==s[i+k];k++);
}
struct segment{
	int l,r,len;
	segment() {}
	segment(int _l,int _r,int L):l(_l),r(_r),len(L) {};
}w[maxx];int cnt,fa[maxx],sz[maxx][3],c[maxx];LL ans[maxx];
bool Comp(const segment & a,const segment & b){return a.len>b.len;}
il int find(int x){if(fa[x]!=x)fa[x]=find(fa[x]);return fa[x];}
il void Insert(int x,int y,int H){fa[x]=y;ans[H]+=sz[y][1]*sz[x][2]+sz[x][1]*sz[y][2],sz[y][1]+=sz[x][1],sz[y][2]+=sz[x][2];}
il void work(){
	scanf("%s%s",s,t);len1=strlen(s),len2=strlen(t);
	s[len1]=‘#‘;for(int i=0;i<len1;++i)c[i]=1;for(int i=0;i<len2;++i)s[len1+i+1]=t[i],c[len1+i+1]=2;
	LEN=len1+len2+1;s[LEN]=0;build_sa(LEN+1);geth();
	for(int i=2;i<=LEN;++i)w[i-1]=segment(sa[i],sa[i-1],height[i]);
	sort(w+1,w+LEN,Comp);for(int i=0;i<LEN;++i)fa[i]=i,sz[i][c[i]]++;
	for(int i=1;i<LEN;++i){
		int x=find(w[i].l),y=find(w[i].r);
		if(x!=y)Insert(x,y,w[i].len);
	}for(int i=LEN;i;--i)A+=ans[i]*i;printf("%lld",A);
}
int main(){
    freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
    work();return 0;
}
时间: 2025-01-02 17:49:10

BZOJ4566:[Haoi2016]找相同字符的相关文章

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的后