题目大意:给出n个关键字和一个模式串,找出模式串和他的反串中一共有多少个关键字。另外模式串是可以压缩输入的:AB[3X]即为ABXXX。
分析:以n个关键字建一颗Tire树,然后正序反序遍历一遍模式串即可。需要注意的是,压缩部分也可能为[ 100X]的形式。。这点卡了好多次RE。。为了节省空间,寻找fail指针部分的队列用数组代替STL库函数了。
实现代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define son_num 26
#define maxn 6100100
char str0[maxn];
char str1[maxn];
struct node
{
node *fail;
node *nex[son_num];
int num; //纪录当前节点对应单词重复的个数
node()
{
fail=NULL;
num=0;
memset(nex,NULL,sizeof(nex));
}
};
node *que[1000*255];
//构造Tire树
void insert(node *root,char *str)
{
node *p=root;
int i=0,index;
while(str[i])
{
index=str[i]-'A';
if(p->nex[index]==NULL)
p->nex[index]=new node();
p=p->nex[index];
i++;
}
p->num++;
}
//寻找失败指针
void build_fail(node *root)
{
int head=0,tail=0;
root->fail=NULL;
que[head++]=root;
while(head!=tail)
{
node *temp=que[tail++]; //取队首元素
node *p=NULL;
for(int i=0;i<son_num;i++)
{
if(temp->nex[i]!=NULL) //寻找当前子树的失败指针
{
if(temp==root) temp->nex[i]->fail=root;
else{
p=temp->fail;
while(p!=NULL)
{
if(p->nex[i]!=NULL) //找到失败指针
{
temp->nex[i]->fail=p->nex[i];
break;
}
p=p->fail;
}
if(p==NULL) //无法获取失败指针,则当前子树的失败指针为根
temp->nex[i]->fail=root;}
que[head++]=temp->nex[i];
}
}
}
}
//询问str中包含多少个关键字
int query(node *root,char *str)
{
int i=0,cnt=0,index,len;
len=strlen(str);
node *p=root;
while(str[i])
{
index=str[i]-'A';
while(p->nex[index]==NULL&&p!=root) //匹配失败
p=p->fail;
p=p->nex[index];
if(p==NULL) //失败指针为根
p=root;
node *temp=p;
while(temp!=root&&temp->num!=-1)
{
//if(temp->num!=0)
cnt+=temp->num;
temp->num=-1;
temp=temp->fail;
}
i++;
}
return cnt;
}
int main()
{
int t,n;
char str[1005];
cin>>t;
while(t--)
{
scanf("%d",&n);
node *root=new node();
for(int i=0;i<n;i++)
{
scanf("%s",str);
insert(root,str);
}
build_fail(root);
scanf("%s",str0);
int cnt=0;
int len=strlen(str0);
for(int i=0;i<len;i++)
{
if(str0[i]=='[')
{
i++;
int num=0;
while(str0[i]>='0'&&str0[i]<='9')
num=num*10+str0[i++]-'0';
for(int j=0;j<num;j++)
str1[cnt++]=str0[i];
i++;
}
if(isupper(str0[i]))
str1[cnt++]=str0[i];
}
str1[cnt]='\0';
int sum=query(root,str1); //纪录正序包含的关键字
for(int i=0;i<cnt/2;i++) //将模式串逆序
{
char c=str1[i];
str1[i]=str1[cnt-1-i];
str1[cnt-1-i]=c;
}
sum+=query(root,str1);
printf("%d\n",sum);
}
return 0;
}