【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;
}
posted @ 2021-06-01 13:21  Jayun  阅读(40)  评论(0编辑  收藏  举报