1. 程式人生 > >【字符串】BZOJ上面幾個AC自動機求最為字串出現次數的題目

【字符串】BZOJ上面幾個AC自動機求最為字串出現次數的題目

參考 none ac自動機 其他 view lose 細節 pen max

(一下只供自己復習用,目的是對比這幾個題,所以寫得不詳細。需要細節的可以參考其他博主)

【BZOJ3172:單詞】

題目:

某人讀論文,一篇論文是由許多(N)單詞組成。但他發現一個單詞會在論文中出現很多次,現在想知道每個單詞分別在論文中出現多少次。N<=200,總單詞長度不超過10^6。

思路:

簡單題,建立AC自動機,插入的時候每個位置都++,代表以當前位置為後綴的字符串的個數,用於fail轉移時累加。然後build得到fail指針;最後從葉子向根累加。

技術分享圖片
#include<bits/stdc++.h>
using namespace
std; 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; }
View Code

【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自動機求最為字串出現次數的題目