[TJOI2013]單詞(AC自動機,樹形dp)
阿新 • • 發佈:2018-12-17
題目
給定個單詞,這個單詞形成一篇文章(單詞間隔斷).統計每個單詞出現次數.
資料範圍:
題解
可以看出每個單詞的貢獻是獨立的,也是對整體有影響的,便想到用樹來統計貢獻.
如果插入一個單詞(如),則每層計數的都.這樣就統計了所有字首的貢獻.如沒有計算.
考慮自動機的指標的轉移:(即是的字尾)
如果在插入所有單詞後建立指標,那麼可以知道一個單詞如的所有後綴(如果字尾有貢獻)會通過指標連結起來
形成了一棵樹,進行樹形即可求出所有的貢獻
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <queue> using namespace std; struct Trie { int next[26]; int fail, cnt, sum; } trie[500005]; int cnt_trie; void insert(string ); void getfail(); struct edge { int next, to; } e[500005]; int head[500005], cnt_adj; void add_edge(int, int); bool vis[500005]; void getsum(int); int find(string); int N; string s[205]; int main(){ cin >> N; int i; for(i = 1; i <= N; i++){ cin >> s[i]; insert(s[i]); } getfail(); getsum(0); for(i = 1; i <= N; i++) cout << find(s[i]) << endl; return 0; } void insert(string s){ int i, len = s.size(); int id, u = 0; for(i = 0; i < len; i++){ id = s[i] - 'a'; if(!trie[u].next[id]) trie[u].next[id] = ++cnt_trie; u = trie[u].next[id]; trie[u].cnt++; } } void getfail(){ int i; queue<int> Q; for(i = 0; i < 26; i++) if(trie[0].next[i]) Q.push(trie[0].next[i]); while(!Q.empty()){ auto u = Q.front(); Q.pop(); for(i = 0; i < 26; i++){ if(trie[u].next[i]){ Q.push(trie[u].next[i]); trie[trie[u].next[i]].fail = trie[trie[u].fail].next[i]; } else trie[u].next[i] = trie[trie[u].fail].next[i]; } add_edge(trie[u].fail, u); } } void getsum(int u){ int i; if(vis[u]) return; vis[u] = true; trie[u].sum = trie[u].cnt; for(i = head[u]; i; i = e[i].next){ int v = e[i].to; getsum(v), trie[u].sum += trie[v].sum; } } int find(string s){ int i, len = s.size(); int id, u = 0; for(i = 0; i < len; i++){ id = s[i] - 'a'; u = trie[u].next[id]; } return trie[u].sum; } void add_edge(int x, int y){ e[++cnt_adj].to = y; e[cnt_adj].next = head[x]; head[x] = cnt_adj; }