【题解】[HAOI2016]找相同字符

[HAOI2016]找相同字符

\(\text{Solution:}\)

第一个想法,考虑对一个串建立自动机,另一个在上面匹配统计答案。

写完发现被样例 hack 了,原因是往字符后面新加入一个后缀字母后的答案不好统计的样子。

考虑直接换成广义 SAM ,求每一个点在两个串里面的出现次数,其对应答案就是 \(siz[i][0]\times siz[i][1]\times (len[i]-len[pa[i]])\)

由于 SAM 其本身带去重功能,所以我们要维护好两个 \(siz.\)

这样会发现一个节点对应的所有后缀答案都是这样的,正确性显然。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
namespace SAM{
	int len[N],pa[N],siz[N][2],ch[N][26],last=1,tot=1;
	vector<int>G[N];
	void insert(const int &c,const int &col){
		int p=last,np=++tot;last=tot;
		len[np]=len[p]+1;siz[np][col]=1;
		for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
		if(!p)pa[np]=1;
		else{
			int q=ch[p][c];
			if(len[q]==len[p]+1)pa[np]=q;
			else{
				int nq=++tot;
				memcpy(ch[nq],ch[q],sizeof ch[q]);
				len[nq]=len[p]+1;pa[nq]=pa[q];pa[q]=pa[np]=nq;
				for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
			}
		}
	}
	void dfs(int x){
		for(auto v:G[x]){
			dfs(v);
			siz[x][0]+=siz[v][0];
			siz[x][1]+=siz[v][1];
		}
	}
	void Build(){
		for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
		dfs(1);
	}
}
char s[N];
using namespace SAM;
signed main(){
	scanf("%s",s+1);
	int n=strlen(s+1);
	for(int i=1;i<=n;++i)insert(s[i]-'a',0);
	scanf("%s",s+1);
	n=strlen(s+1);
	last=1;
	for(int i=1;i<=n;++i)insert(s[i]-'a',1);
	Build();
	int Ans=0;
	for(int i=1;i<=tot;++i){
		if(!siz[i][0]||!siz[i][1])continue;
		Ans+=(len[i]-len[pa[i]])*siz[i][0]*siz[i][1];
	}
	cout<<Ans<<endl;
	return 0;
}
posted @ 2021-08-23 18:38  Refined_heart  阅读(36)  评论(0编辑  收藏  举报