【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;
}