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

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

题面

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

分析

此题有一个比较繁琐的后缀数组做法,但是用广义SAM可以秒杀。

把两个串建成广义SAM,对于每个后缀,记录\(endpos\)集合中落在第一个串中和第二个串中的位置个数,记为\(cnt_{x,0},cnt_{x,1}\)。

对于自动机上的每个节点\(x\),出现位置方案数的贡献是\(cnt_{x,0} \cdot cnt_{x,1}\),注意这个节点代表了一系列不同子串,还要乘上这个位置代表的本质不同子串个数\(len(x)-len(link(x))\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 200000
#define maxc 26
using namespace std;
typedef long long ll;
int n,m;
char s[maxn+5],t[maxn+5];
struct SAM{
#define len(x) (t[x].len)
#define link(x) (t[x].link)
	struct node{
		int len;
		int link;
		int ch[maxc+1];
		int cnt[2];
	}t[maxn*4+5];
	const int root=1;
	int ptr=1,last=root;
	void extend(int c,int type){
		 int p=last,cur=++ptr;
		 len(cur)=len(p)+1;
		 if(type<=1) t[cur].cnt[type]++;
		 while(p&&!t[p].ch[c]){
		 	t[p].ch[c]=cur;
		 	p=link(p);
		 }
		 if(p==0) link(cur)=root;
		 else{
		 	int q=t[p].ch[c];
		 	if(len(p)+1==len(q)) link(cur)=q;
		 	else{
		 		int clo=++ptr;
		 		link(clo)=link(q);
		 		len(clo)=len(p)+1;
		 		for(int i=0;i<=maxc;i++) t[clo].ch[i]=t[q].ch[i];
		 		link(q)=link(cur)=clo;
		 		while(p&&t[p].ch[c]==q){
		 			t[p].ch[c]=clo;
		 			p=link(p);
				 }
			 }
		 }
		 last=cur;
	}
	void topo_sort(){
		static int in[maxn*4+5];
		queue<int>q;
		for(int i=2;i<=ptr;i++) in[link(i)]++;
		for(int i=2;i<=ptr;i++) if(!in[i]) q.push(i);
		while(!q.empty()){
			int x=q.front();
			q.pop();
			in[link(x)]--;
			for(int i=0;i<=1;i++) t[link(x)].cnt[i]+=t[x].cnt[i];
			if(in[link(x)]==0) q.push(link(x));
		}
	}
	ll calc(){
		ll ans=0;
		for(int i=2;i<=ptr;i++) ans+=1ll*(t[i].len-t[link(i)].len)*t[i].cnt[0]*t[i].cnt[1];
		return ans;
	}
}T;
int main(){
	scanf("%s",s+1);
	scanf("%s",t+1);
	n=strlen(s+1),m=strlen(t+1);
	for(int i=1;i<=n;i++) T.extend(s[i]-‘a‘,0);
//	T.extend(26,2);
	T.last=T.root;
	for(int i=1;i<=m;i++) T.extend(t[i]-‘a‘,1);
	T.topo_sort();
	printf("%lld\n",T.calc());
}

原文地址:https://www.cnblogs.com/birchtree/p/12628351.html

时间: 2024-08-29 03:33:53

[HAOI2016]找相同字符(广义SAM)的相关文章

[HAOI2016]找相同字符 广义后缀自动机_统计出现次数

题目描述:给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式输入格式:两行,两个字符串 s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式:输出一个整数表示答案 题解:对 $2$ 个字符串建立一个广义后缀自动机.实际上,广义后缀自动机就是对多个字符串用一个自动机加以维护.每加入完毕一个字符串时,将 $last$ 设为 $1$.插入字符时,若 $ch[la

P3181 [HAOI2016]找相同字符

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

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,字符串中只有小写字母

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

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

4566: [Haoi2016]找相同字符 SAM

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

[HAOI2016]找相同字符(SAM)

题目描述 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式 输入格式: 两行,两个字符串s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式: 输出一个整数表示答案 输入输出样例 输入样例#1: 复制 aabb bbaa 输出样例#1: 复制 10 这到题目是vj上一道题目的简化版对第一个串建立自动机 在拓扑一边球每个状态串的出现次数然后然第二个串在树上

luogu3181 [HAOI2016]找相同字符

建广义\(\mathrm{SAM}\),同时对每个串在每个节点处分别维护其\(\mathrm{endpos}\)集合大小,记广义\(\mathrm{SAM}\)上的节点\(u\)的两个串的\(\mathrm{endpos}\)集合大小分别为\(siz_{u,0},siz_{u,1}\),则 \[ \mathrm{Answer}=\sum_{i=1}^{tot} siz_{i,0}\times siz_{i,1} \times (\mathrm{Len}(u)-Len(fa_u)) \] #inc

[HAOI2016]找相同字符

题目大意: 给你两个字符串a和b,要求从a和b中各取出一个相等的子串,问不同的取法有多少种. 思路: 对于a串建立SAM,然后DP求出每个状态Right集合的大小. 然后把b串放进去匹配,对于每一个匹配到的结点p,它的每一个Right状态都可以匹配一个长度为tmp-s[s[p].link].len的串,那么将s[p].right*(tmp-s[s[p].link].len)计入答案. 统计每个状态被匹配的次数,并累加进它Parent中. 将s[top[i]].right*f[top[i]]*(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的后