生活随笔
收集整理的這篇文章主要介紹了
【bzoj4566】[Haoi2016]找相同字符【后缀自动机】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目傳送門
題解:在文本串上建后綴自動機,用模式串在后綴自動機上跑。掃一遍模式串,在后綴自動機上走,走不了就跳fail再走。
走的過程中,維護模式串與文本串匹配的最大長度,并且統計答案。
怎么統計答案呢?
我們知道,狀態x表示的字符串的長度為len[x]len[x],對應了len[x]?len[fail[x]]len[x]?len[fail[x]]個子串(每個子串都是當前狀態的前綴)。因此,狀態x表示的子串總數為(len[x]?len[fail[x]])?cnt[x](len[x]?len[fail[x]])?cnt[x]。同時,狀態x的fail一定是x表示字符串的后綴。因此,如果當前在后綴自動機上走到了狀態x,最大匹配長度為maxl,就把答案加上x沿fail到根節點上所有狀態表示的子串總數。注意x這個狀態是特殊的,它與模式串做能匹配的子串個數是(maxl?len[fail[x]])?cnt[x](maxl?len[fail[x]])?cnt[x]。其他跳fail到根節點路徑上的狀態則直接加上其表示的子串總數,因為這些狀態是與模式串完全匹配的。(難以描述,自行理解一下吧)
具體實現詳見代碼。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=
200005;
int l1,l2,a[N*
2];
ll ans=
0;
char s1[N],s2[N];
struct SAM{
int last,tot,len[N*
2],fail[N*
2],cnt[N*
2],ch[N*
2][
26],c[N*
2];SAM(){last=tot=
1;}
void insert(
int x){
int p=last,np=++tot;len[np]=len[p]+
1;last=np;
for(;p&&!ch[p][x];p=fail[p]){ch[p][x]=np;}
if(!p){fail[np]=
1;}
else{
int q=ch[p][x];
if(len[q]==len[p]+
1){fail[np]=q;}
else{
int nq=++tot;len[nq]=len[p]+
1;
memcpy(ch[nq],ch[q],
sizeof(ch[q]));fail[nq]=fail[q];fail[q]=fail[np]=nq;
for(;p&&ch[p][x]==q;p=fail[p]){ch[p][x]=nq;}}}cnt[np]=
1;}
void init(){
for(
int i=
1;i<=tot;i++){c[len[i]]++;}
for(
int i=
1;i<=l1;i++){c[i]+=c[i-
1];}
for(
int i=
1;i<=tot;i++){a[c[len[i]]--]=i;}
for(
int i=tot;i>=
1;i--){cnt[fail[a[i]]]+=cnt[a[i]];}}
void get(
int now,
int l){
while(len[fail[now]]>l){now=fail[now];}
while(fail[now]){ans+=
1LL*cnt[now]*(l-len[fail[now]]);l=len[fail[now]];now=fail[now];}}
}sam;
int main(){
scanf(
"%s%s",s1,s2);l1=
strlen(s1);l2=
strlen(s2);
for(
int i=
0;i<l1;i++){sam.insert(s1[i]-
'a');}sam.init();
int now=
1,len=
0;
for(
int i=
0;i<l2;i++){
while(now&&!sam.ch[now][s2[i]-
'a']){now=sam.fail[now];}
if(!now){now=
1;len=
0;}
else{len=min(len,sam.len[now])+
1;now=sam.ch[now][s2[i]-
'a'];}sam.get(now,len);}
printf(
"%lld\n",ans);
return 0;
}
轉載于:https://www.cnblogs.com/2016gdgzoi471/p/9476890.html
總結
以上是生活随笔為你收集整理的【bzoj4566】[Haoi2016]找相同字符【后缀自动机】的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。