1. 程式人生 > 其它 >【題解】[HAOI2016]找相同字元

【題解】[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;
}