1. 程式人生 > >hihocoder 1465 循環串匹配問題(後綴自動機)

hihocoder 1465 循環串匹配問題(後綴自動機)

神奇 說明 for 自動 () 狀態 出現 str pac

後綴自動機感覺好萬能

tries圖和ac自動機能做的,後綴自動機很多也都可以做

這裏的循環匹配則是後綴自動機能做的另一個神奇功能

循環匹配意思就是S是abba, T是abb

問‘abb‘, ‘bba‘,‘bab‘在S中出現過多少次。

我們先把T的末尾循環加一遍,變成abbab

然後把問題轉換成,求T的每個後綴和S的最長公共子串

如果最長公共子串的長度大於等於T的長度,那麽就說明這個後綴匹配成功

做法就是先對S建立一個後綴自動機,然後記錄一個狀態

(u, l),u表示當前在後綴自動機匹配的位置,l表示最長公共子串的長度

考慮轉移的話,就是

如果下一個位置可以匹配,那麽u就到相應的位置,l = l+1

答案更新的時候要註意,如果l大於T的長度len,就需要順著link往前走到第一個能匹配的位置,即第一個maxlen[x] >= len的地方,然後答案加上endpos[x],不然會丟一部分答案。

如果下一個位置不可以匹配,那麽u就順著link邊走,走到第一個能匹配的地方,如果找不到,那u就設成起點,l為0

還有一個問題就是串重復的情況,比如說T是aa,那麽擴充就會變成aaa,aa和aa重復。

如果串重復的話,那麽必定會到同一個狀態,所以一個狀態標記一下,只更新一遍答案就可以了

#include <iostream>
#include <cstring>
#include 
<cstdio> #include <queue> #include <map> using namespace std; int n = 0, len, st; const int maxL = 1e6 + 100; int maxlen[2*maxL], minlen[2*maxL], trans[2*maxL][27], slink[2*maxL], lab[2*maxL], son[2*maxL], endpos[2*maxL]; map<int, bool> vis; int new_state(int _maxlen, int _minlen, int
*_trans, int _slink){ maxlen[n] = _maxlen; minlen[n] = _minlen; for(int i = 0; i < 26; i++){ if(_trans == NULL) trans[n][i] = -1; else trans[n][i] = _trans[i]; } slink[n] = _slink; return n++; } int add_char(char ch, int u){ int c = ch - a; int z = new_state(maxlen[u]+1, -1, NULL, -1); lab[z] = 1; int v = u; while(v != -1 && trans[v][c] == -1){ trans[v][c] = z; v = slink[v]; } if(v == -1){ minlen[z] = 1; slink[z] = 0; return z; } int x = trans[v][c]; if(maxlen[v] + 1 == maxlen[x]){ minlen[z] = maxlen[x] + 1; slink[z] = x; return z; } int y = new_state(maxlen[v] + 1, -1, trans[x], slink[x]); slink[y] = slink[x]; minlen[x] = maxlen[y] + 1; slink[x] = y; minlen[z] = maxlen[y] + 1; slink[z] = y; int w = v; while(w != -1 && trans[w][c] == x){ trans[w][c] = y; w = slink[w]; } minlen[y] = maxlen[slink[y]] + 1; return z; } char str[maxL]; int main() { cin>>str; st = new_state(0, 0, NULL, -1); int len = strlen(str); for(int i = 0; i < len; i++) { st = add_char(str[i], st); } for(int i = 1; i <= n; i++) son[slink[i]]++; queue<int> Q; for(int i = 1; i <= n; i++) if(son[i] == 0) Q.push(i), endpos[i] = 1; while(!Q.empty()){ int x = Q.front(); Q.pop(); if(x == 0) continue; int y = slink[x]; son[y]--; endpos[y] += endpos[x]; if(son[y] == 0){ if(lab[y]) endpos[y]++; Q.push(y); } } int T; cin>>T; while(T--){ vis.clear(); cin>>str; int len = strlen(str), ylen = len; for(int i = len; i < 2*len-1; i++) str[i] = str[i-len]; len = 2*len-1; int u = 0, l = 0, ans = 0; for(int i = 0; i < len; i++){ int c = str[i] - a; if(trans[u][c] != -1){ u = trans[u][c]; l++; } else { int y = slink[u]; while(y != -1){ if(trans[y][c] != -1){ l = maxlen[y] + 1; u = trans[y][c]; break; } u = y; y = slink[u]; } if(y == -1) { u = 0; l = 0; } } if(l >= ylen){ int y = slink[u]; while(maxlen[y] >= ylen) { u = y; y = slink[u]; l = maxlen[u]; } if(vis[u]) continue; vis[u] = 1; ans += endpos[u]; } } cout<<ans<<endl; } return 0; }

hihocoder 1465 循環串匹配問題(後綴自動機)