小張最近在忙畢設,因此一直在讀論文。一篇論文是由許多單詞組成但小張發現一個單詞會在論文中出現不少次,他想知道每一個單詞分別在論文中出現了多少次。spa
輸入格式:code
第一行一個整數N,表示有N個單詞。接下來N行每行一個單詞,每一個單詞都由小寫字母(a-z)組成。(N≤200)blog
輸出格式:string
輸出N個整數,第i行的數表示第i個單詞在文章中出現了多少次。io
30%的數據, 單詞總長度不超過10^3class
100%的數據,單詞總長度不超過10^6queue
本身xjb YY了一個作法竟然1Ahhh統計
首先應該一眼就能看出是AC自動機。數據
那麼咱們先把全部串的AC自動機搞出來,而後記錄下他們拼起來的串,用隨便一個字符分隔di
暴力枚舉每個串,把通過的路徑上的權值$+1$,表示該位置表明的串又多出現了一次。
這樣咱們就統計出了與它如出一轍的串的出現次數。
還有一種狀況,即當它做爲某些串的後綴出現。
此時,根據AC自動機的性質不難發現,咱們要求的答案即爲該節點在$fail$樹上子樹的和
而後直接暴力把$fail$樹建出來,樹形DP統計答案便可
就是跑的有點慢
// luogu-judger-enable-o2 // luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<queue> using namespace std; const int MAXN = 1e6 + 100, B = 28; int T; char s[MAXN], a[MAXN]; int fail[MAXN], ch[MAXN][28], val[MAXN], tot = 0, root = 0; void insert(char *s) { int N = strlen(s + 1); int now = root; for(int i = 1; i <= N; i++) { int x = s[i] - 'a'; if(!ch[now][x]) ch[now][x] = ++tot; now = ch[now][x]; val[now]++; } } vector<int> v[MAXN]; void GetFail() { queue<int> q; for(int i = 0; i < B; i++) if(ch[root][i]) q.push(ch[root][i]); while(!q.empty()) { int p = q.front(); q.pop(); for(int i = 0; i < B; i++) { if(ch[p][i]) fail[ch[p][i]] = ch[fail[p]][i], q.push(ch[p][i]); else ch[p][i] = ch[fail[p]][i]; } v[fail[p]].push_back(p); } } void GetVal(int x) { for(int i = 0; i < v[x].size(); i++) GetVal(v[x][i]), val[x] += val[v[x][i]]; } void GetAns(char *s) { int N = strlen(s + 1), now = root, ans = 0; for(int i = 1; i <= N; i++) { int x = s[i] - 'a'; if(x == 26) printf("%d\n", val[now]), now = root, ans = 0; now = ch[now][x]; } printf("%d", val[now]); } int main() { //freopen("a.in", "r", stdin); scanf("%d", &T); for(int i = 1; i <= T; i++) { scanf("%s", s + 1); insert(s); s[0] = 'z' + 1; strcat(a, s); } GetFail(); GetVal(0); GetAns(a); return 0; }