题目大意


给出一些模式串和一个被匹配串,若被匹配串包含模式串或其反转,则称该模式串感染了被匹配串。问有多少个模式串感染了被匹配串。

思路


AC自动机。用模式串去匹配被匹配串及其反转。

代码


#include<iostream>
#include<stdio.h>
using namespace std;

#define T_SIZE 5100000
#define P_SIZE 1000
#define TOTAL_P 250

struct trie
//利用结构体来封装字典树的节点。
{
	trie* next[26];
	trie* fail;
	int num;
	trie()
	//构造函数。
	{
		for(int i=0;i<26;i++)
			next[i]=NULL;
		fail=NULL;
		num=0;
	}
};

char T[T_SIZE+1];
char temp_T[T_SIZE+1];
char P[P_SIZE+1];
trie* q[TOTAL_P*P_SIZE];

void insert(trie* root,char* s)
//将字符串s所表示的单词插入到字典树中。
{
	trie* p=root;
	for(int i=0;s[i]!='\0';i++)
	{
		if(p->next[s[i]-'A']==NULL)
			p->next[s[i]-'A']=new trie;
		p=p->next[s[i]-'A'];
	}
	p->num++;
}

void  build_ac_automation(trie* root)
//利用广搜构建失败指针 
{
	int head=0,tail=0;
	q[tail++]=root;
	while(head!=tail)
	{
		trie* front=q[head++];
		//front为队头元素 
		for(int i=0;i<26;i++)
			if(front->next[i]!=NULL)
			//遍历队头元素的子节点 
			{
				trie* p=front->fail;
				while(p!=NULL)
				//只有根节点的失败指针为NULL 
				{
					if(p->next[i]!=NULL)
					//顺着失败指针往回走,直至某个节点,其拥有一个字母为'A'+i的子节点。 
					{
						front->next[i]->fail=p->next[i];
						break;
					}
					p=p->fail;
				}
				if(p==NULL)
				//p==NULL说明顺着失败指针往回走的过程中没有找到合适的节点,所以将失败指针指向根节点。 
					front->next[i]->fail=root;
				q[tail++]=front->next[i];
			}
	}
}

int ac_find(trie* root,char* T)
{
	trie* p=root;
	int sum=0;
	for(int i=0,len=strlen(T);i<len;i++)
	{
		while(p->next[T[i]-'A']==NULL&&p!=root)
		//若当前节点的没有一个字符为T[i]的儿子且当前节点不是根节点
		//通俗的讲,就是顺着失败指针往回走,直至找到合适的节点或根节点为止。 
			p=p->fail;
		if(p->next[T[i]-'A']!=NULL)
		//p->next[T[i]-'A']==NULL说明没找到合适的节点,p指针指在根节点上。
			p=p->next[T[i]-'A']; 
		trie* temp=p;
		while(temp!=root&&temp->num!=-1)
		//顺着失败指针往回走,一直到根节点。 
		{
			sum+=temp->num;
			//若当前节点的num不为0,则说明以当前节点字母结尾的单词出现过一次。
			//此单词是以上一次循环的节点单词为结尾的单词的子集。 
			temp->num=-1;
			//标记num为-1,避免重复计算。 
			temp=temp->fail;
		}
	}
	return sum;
}

void get_T(char* temp_T)
//解压母串 
{
	int len=strlen(temp_T),len_T=0;
	for(int i=0,num;i<len;i++)
		if(temp_T[i]>='A'&&temp_T[i]<='Z')
			T[len_T++]=temp_T[i];
		else if(temp_T[i]=='[')
			num=0;
		else if(temp_T[i]>='0'&&temp_T[i]<='9')
			num=(num+(temp_T[i]-'0'))*10;
		else if(temp_T[i]==']')
		{
			num/=10;
			for(int k=0;k<num-1;k++)
				T[len_T++]=temp_T[i-1];
		}
	T[len_T]='\0';
}

void reverse(char* str)
//反转字符串 
{
	int len=strlen(str);
	for(int i=0;i<len/2;i++)
		swap(str[i],str[len-i-1]);
}

int main()
{
	int t;
	for(scanf("%d",&t);t>0;t--)
	{
		trie* root=new trie;
		int n;
		scanf("%d",&n);
		getchar();
		for(int i=0;i<n;i++)
		{
			gets(P);
			insert(root,P);
		}
		build_ac_automation(root);
		gets(temp_T);
		get_T(temp_T);
		int res=0;
		res=ac_find(root,T);
		reverse(T);
		res+=ac_find(root,T);
		printf("%d\n",res);
	}
}