【題解】[HAOI2016]找相同字元
阿新 • • 發佈:2021-08-23
\(\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; }