【YBTOJ】【Luogu P3966】[TJOI2013]单词
链接:
题目大意:
求每个单词分别在论文中出现了多少次。
正文:
对于整篇“论文”,也就是文本串,其实就是各个“单词”,即模式串,连接而成,而且有分隔符。
那么接下来就与二次加强的 AC 自动机板子一模一样了:因为直接跳失配指针会被卡,所以建 fail
树,然后跑 DFS 或者拓扑。
代码:
const int N = 3e6 + 10, M = 160; inline ll Read() { ll x = 0, f = 1; char c = getchar(); while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') f = -f, c = getchar(); while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar(); return x * f; } int n; namespace AC { int t[N][30], id[N], val[N], cnt[N], fail[N]; int tot; void Insert(char *s, int I) { int p = 0, len = strlen(s); for (int i = 0; i < len; i++) { int ch = s[i] - 'a'; if (!t[p][ch]) t[p][ch] = ++tot; p = t[p][ch]; } id[I] = p; } queue <int> q; vector <int> e[N]; void Build() { while(!q.empty()) q.pop(); for (int i = 0; i < 26; i++) if (t[0][i]) q.push(t[0][i]); while (!q.empty()) { int p = q.front(); q.pop(); for (int i = 0; i < 26; i++) if (t[p][i]) fail[t[p][i]] = t[fail[p]][i], q.push(t[p][i]); else t[p][i] = t[fail[p]][i]; } for (int i = 1; i <= tot; i++) e[fail[i]].push_back(i); } void Query(char *s) { int p = 0, ans = 0, len = strlen(s); for (int i = 0; i < len; i++) { p = t[p][s[i] - 'a']; val[p]++; } } void DFS(int u) { for (int i = 0, v; i < e[u].size(); i++) DFS(v = e[u][i]), val[u] += val[v]; } } char s[N], t[N]; int main() { n = Read(); for (int i = 1; i <= n; i++) scanf ("%s", s), AC::Insert(s, i), strcat(t, s), strcat(t, "@"); AC::Build(); // puts(t); AC::Query(t); AC::DFS(0); for (int i = 1; i <= n; i++) printf ("%d\n", AC::val[AC::id[i]]); return 0; }