bzoj3942 AC自动机

题意:有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程。

Sample Input
whatthemomooofun
moo

Sample Output
whatthefun

题解:将T串插入到AC自动机中,对于S串每个字符的匹配,如果当前节点被标记是T的终止节点就把AC自动机上匹配到的节点更新到T串前一个字符。
用一个数组记录每一个字符匹配到的AC自动机上的节点号,用一个栈记录答案。

AC代码:

#include
using namespace std;
const int maxn=1e6+50;
const int maxm=1e6+50;
int n;
char s[maxn];char t[maxm];
int rec[maxn];
int fin[maxn];int poi;int nn;
struct ACAM//插入的是一个个模式串,询问的t是总的文本串 
{
     
	int trie[maxn][27];int cnt=0;//trie,cnt是当前节点总数量 
	int end[maxn];//标记这个结点是不是字符串结尾
	int fail[maxn];//fail指针
	int siz[maxn];
	void insert(char *s)
	{
     
		int p=0;
		int len=strlen(s);
		for(int i=0;i<len;i++)
		{
     
			int k=s[i]-'a';
			if(!trie[p][k]) trie[p][k]=++cnt;
			p=trie[p][k];
		//	rec[i]=p;
		}
		int lst=p;//lst是插入后最终所在的节点 
		end[lst]=1;
	}
	void build()
	{
     
		queue<int> que;
		memset(fail,0,sizeof(fail));//先清空fail数组 
		for(int i=0;i<26;i++) if(trie[0][i]) que.push(trie[0][i]);//将根节点的子节点入队 
		while(!que.empty())
		{
     
			int k=que.front();que.pop();
			for(int i=0;i<26;i++)
			{
     
				if(trie[k][i])
				{
     
					fail[trie[k][i]]=trie[fail[k]][i];//因为路径压缩过,所以可以直接连边 
					if(end[fail[trie[k][i]]]==1) end[trie[k][i]]=1;
					que.push(trie[k][i]);
				}
				else trie[k][i]=trie[fail[k]][i];//找不到时直接赋值,类似路径压缩。连到上一次最长的能接上字符c的节点 
			}
		}
	 }
	void solve()
	{
     
		poi=0;
		int len=strlen(s);
		int p=0;
		for(int i=0;i<len;i++)
		{
     
			int zz=s[i]-'a';
			p=trie[p][zz];
		//	cout<
			fin[++poi]=i;
			if(end[p])
			{
     
			//	cout<
				poi=poi-nn;
				p=rec[fin[poi]];
			}
			rec[i]=p;
		//	else fin[++poi]=i;
		}
		return;
	}
}ac;
int main()
{
     
	scanf("%s",s);
	scanf("%s",t);
	nn=strlen(t);
	ac.insert(t);
	ac.build();
	ac.solve();
	for(int i=1;i<=poi;i++)
	{
     
		printf("%c",s[fin[i]]);
	}
	printf("\n");
	return 0;
}

你可能感兴趣的:(字符串)