HDU6096.String (思维+AC自动机)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6096
在这里插入图片描述在这里插入图片描述
题意:给出几组字符串W,又给出几个字符串的前缀p和后缀s,要求在前面给出的几组W字符串中找出前缀后缀同样出现p和s的数目
解题思路:
有两种,都很巧妙地拼凑出AC自动机解法
第一种:
把w字符串保存为w+’{’+w的形式,这样’{‘的左右两边就出现了后缀+{+前缀的情况,{用来分割开后缀与前缀,至于为什么用{,应该{的ASCII码值正好在z的后面一位,利用构造字典树。
然后将要寻找的前缀p和后缀s构造成s+{+p的形式,进行建树。
然后就在建好的字典树中利用ac自动机去寻找上面w的最大匹配数就行了
PS:①注意匹配成功的条件不仅仅是找到,还要求w的原长要大于等于p+s的 长度,以免出现aa aa匹配到了aaa的情况。同时要注意w的原长=(w的现在长度-1)/2,p+s长度为现在长度减1
②注意在构建字典树的时候,cntword[pos]不再是++了,而是直接令它=1,不然当出现两组p s相同的情况时候,会导致cntword[pos]=2,对于每一组的p s多加了1
第二种:
仍然是构造ac自动机的条件,将p和s依次插入字典树,也就是先放入p[0]再放入s[len-1],再放入p[1],然后放入s[len-2],构造出这种交错的情况,如果前缀和后缀数量不同,就用{来填充。
对于w串也同样采用这种方法构造,然后在字典树中寻找最大匹配就行。

第一种AC代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<queue>
#include<string.h>
using namespace std;
int t,n,q;
int cnt;
string w[100100],p,s;
int trie[3222200][27];
int cntword[3222200];
int fail[3222200];
int length[3222200];
int ans[3222200];
int pos[100100];
void init()
{
	memset(trie,0,sizeof(trie));
	memset(cntword,0,sizeof(cntword));
	memset(fail,0,sizeof(fail));
	memset(length,0,sizeof(length));
	memset(ans,0,sizeof(ans));
	cnt=0;
}
int insert(string str)
{
	int len=str.size();
	int pos=0;
	for(int i=0;i<len;i++)
	{
		int next=str[i]-'a';
		if(!trie[pos][next])
		{
			trie[pos][next]=++cnt;
			length[cnt]=i+1;
		}
		pos=trie[pos][next];
	}
	cntword[pos]=1;      //这里不再是++了,因为如果有两个q串相同,不应该加2,而仍应该加1
	return pos;
}
void getfail()
{
	queue<int> Q;
	for(int i=0;i<=26;i++)
		if(trie[0][i])
		{
			fail[trie[0][i]]=0;
			Q.push(trie[0][i]);
		}
	int now;
	while(!Q.empty())
	{
		now=Q.front(); Q.pop();
		for(int i=0;i<=26;i++)
		{
			if(trie[now][i])
			{
				fail[trie[now][i]]=trie[fail[now]][i];
				Q.push(trie[now][i]);
			}
			else
				trie[now][i]=trie[fail[now]][i];
		}
	}
}
void query(string str)
{
	int len=str.size();
	int now=0;
	for(int i=0;i<len;i++)
	{
		now=trie[now][str[i]-'a'];
		for(int j=now;j;j=fail[j])
		{
			if(cntword[j]&&length[j]<=((len-1)/2+1))
			{
				ans[j]+=cntword[j];
			}
			//if(length[j]<=((len-1)/2+1))         //这里一定要注意匹配长度不应该是len,而应该是原长+一个‘{’
			//{
			//	ans[j]++;
			//}
		}
	}
}		
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		init();
		scanf("%d%d",&n,&q);
		for(int i=0;i<n;i++)
		{
			cin>>w[i];
			string temp="{";   //这边一定要注意,用的符号要刚好大于z,不然runtime error,而且字典树范围不再在26,开到27
			w[i]+=temp+w[i];
		}
		for(int i=0;i<q;i++)
		{
			cin>>p>>s;
			string temp="{";
			s+=temp+p;
			pos[i]=insert(s);
		}
		getfail();
		for(int i=0;i<n;i++)
			query(w[i]);
		for(int i=0;i<q;i++)
			printf("%d\n",ans[pos[i]]);
	}
	return 0;
}

参考:https://blog.csdn.net/lxy767087094/article/details/77163275
https://blog.csdn.net/static_ricardo/article/details/77072865

原创文章 65 获赞 3 访问量 2100

猜你喜欢

转载自blog.csdn.net/littlegoldgold/article/details/105071390
今日推荐