洛谷 3966 libreoj10060 [TJOI2013]单词 题解

博客观赏效果更佳

题意简述

给定 n n 个单词,对每个单词求出 n n 个单词中总共包含多少这个单词(自己也算,也就是说答案至少为 1 1 )。比如三个单词分别是 a , a a , a a a a,aa,aaa ,那么 a a aa 出现 3 3 次(自己一次, a a a aaa 中两次)。

思路

每个字符接在一起,中间插入一个特殊字符,然后跑一遍 A C AC 自动机即珂。

具体的思维过程

A C AC 自动机的模板问题:给定一个文本串,和一些模式串,求每个模式串在文本串中出现了多少次。

实际上我们珂以把这个问题转化成模板问题。由于我们的匹配不能跨单词,只能限于每个单词之间。最好的解决方法就是构造这样的情况:跨单词肯定得失配

这好办,只要把所有单词接在一起,然后在中间加一个特殊字符,使得它不等于 [ a , z ] [a,z] 中的任意一个,那跨单词的时候肯定就失配了(因为模式串中只有 [ a , z ] [a,z] )比如说设成 A S C I I ASCII 中的字符 z + 1 'z'+1 ,经验证,这个字符是左大括号 { \{ 。然后写 A C AC 自动机的时候,就当字母有 27 27 个字母去建树即珂。这题就被完美的套成了模板解决了。

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
	#define N 212345

	#define F(i,l,r) for(int i=l;i<=r;++i)
	#define D(i,r,l) for(int i=r;i>=l;--i)
	#define Fs(i,l,r,c) for(int i=l;i<=r;c)
	#define Ds(i,r,l,c) for(int i=r;i>=l;c)
	#define Tra(i,u) for(int i=G.Start(u);~i;i=G.Next(i))
	#define MEM(x,a) memset(x,a,sizeof(x))
	#define FK(x) MEM(x,0)

	int id[N];
	class AC_Automaton
	{
	public:
		int tr[N][27];
		int tot;

		void Init()
		{
			FK(tr);
			tot=1;
		}
		int Insert(char s[])
		{
			int pos=1;
			F(i,0,INT_MAX)
			{
				if (!s[i]) break;

				int c=s[i]-'a';
				if (!tr[pos][c])
				{
					tr[pos][c]=++tot;
				}
				pos=tr[pos][c];
			}
			return pos;
		}

		int fail[N];
		queue<int> Q;
		vector<int> G[N];void Add(int u,int v){G[u].push_back(v);}
		void BuildFail()
		{
			while(!Q.empty()) Q.pop();

			F(i,0,26) tr[0][i]=1;
			Q.push(1);
			while(!Q.empty())
			{
				int u=Q.front();Q.pop();

				F(i,0,26)
				{
					if (tr[u][i])
					{
						fail[tr[u][i]]=tr[fail[u]][i];
						Q.push(tr[u][i]);
					}
					else tr[u][i]=tr[fail[u]][i];
				}
			}

			F(i,2,tot) Add(fail[i],i);
		}

		int size[N];
		void DFS(int u)
		{
			F(i,0,(int)G[u].size()-1)
			{
				int v=G[u][i];
				DFS(v);
				size[u]+=size[v];
			}
		}
		void Query(char s[])
		{
			int pos=1;
			F(i,0,INT_MAX)
			{
				if (!s[i]) break;

				int c=s[i]-'a';
				pos=tr[pos][c];
				++size[pos];
			}
			DFS(1);
		}
	}AC;

	int n;
	char s[N];
	char T[N*10];
	void Input()
	{
		scanf("%d",&n);
		AC.Init();
		FK(T);
		F(i,1,n)
		{
			scanf("%s",s);
			id[i]=AC.Insert(s);
			strcat(T,s);
			strcat(T,"{");//'{'='z'+1
		}
	}
	void Soviet()
	{
		// printf("T=%s\n",T);
		AC.BuildFail();
		AC.Query(T);
		F(i,1,n)
		{
			printf("%d\n",AC.size[id[i]]);
		}
	}
	void IsMyWife()
	{
		Input();
		Soviet();
	}
}
int main()
{
	Flandre_Scarlet::IsMyWife();
	getchar();getchar();
	return 0;
}
发布了210 篇原创文章 · 获赞 8 · 访问量 9002

猜你喜欢

转载自blog.csdn.net/LightningUZ/article/details/103327218