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