【字符串】BZOJ上面幾個AC自動機求最為字串出現次數的題目
阿新 • • 發佈:2018-05-07
參考 none ac自動機 其他 view lose 細節 pen max
(一下只供自己復習用,目的是對比這幾個題,所以寫得不詳細。需要細節的可以參考其他博主)
【BZOJ3172:單詞】
題目:
某人讀論文,一篇論文是由許多(N)單詞組成。但他發現一個單詞會在論文中出現很多次,現在想知道每個單詞分別在論文中出現多少次。N<=200,總單詞長度不超過10^6。
思路:
簡單題,建立AC自動機,插入的時候每個位置都++,代表以當前位置為後綴的字符串的個數,用於fail轉移時累加。然後build得到fail指針;最後從葉子向根累加。
#include<bits/stdc++.h> using namespaceView Codestd; const int maxn=1000010; char c[maxn]; int ans[maxn],pos[210],N; struct Trie { int ch[maxn][26],cnt,times,fail[maxn],q[maxn],head,tail; Trie(){ cnt=times=head=tail=0; } int insert(){ int Now=0,L=strlen(c+1); for(int i=1;i<=L;i++){ if(!ch[Now][c[i]-‘a‘]) ch[Now][c[i]-‘a‘]=++cnt; Now=ch[Now][c[i]-‘a‘]; ans[Now]++; } return Now; } void build() { for(int i=0;i<26;i++) if(ch[0][i]) q[++head]=ch[0][i]; while(tail<head){ int Now=q[++tail];for(int i=0;i<26;i++){ if(ch[Now][i]){ fail[ch[Now][i]]=ch[fail[Now]][i]; q[++head]=ch[Now][i]; } else ch[Now][i]=ch[fail[Now]][i]; } } for(int i=tail;i>=1;i--) ans[fail[q[i]]]+=ans[q[i]]; } }T; int main() { scanf("%d",&N); for(int i=1;i<=N;i++){ scanf("%s",c+1); pos[i]=T.insert(); } T.build(); for(int i=1;i<=N;i++) printf("%d\n",ans[pos[i]]); return 0; }
【BZOJ2434阿貍的打字機】:
題目:
給定N個字符串。現在又Q個問題,每次問題給出(i,j),求第i個字符串在第j個字符串裏出現的次數。 1<=N<=10e5;1<=M<=10e5;輸入總長<=10e5
思路:
因為上一題是單次訊問,而且是整體求,所以一次拓撲倒序累加即可,但是此題是多次詢問,而且是針對Trie樹上代表的兩個字符串之間的包含次數,不能整體法。 正解: 1,先建立AC自動機;2,得到fail樹;3,對fail樹進行DFS得到DFS序;4,在Trie樹上dfs求解。
【BZOJ3881】 題意: Bob有個字符串集合T,一開始為空,現在有兩種操作:1,Bob的集合新增加一個字符串str; 2,Alice給出字符串x,問集合T中多少字符串包含x。1e6級別。【字符串】BZOJ上面幾個AC自動機求最為字串出現次數的題目