题意:求n个子串在一个字符串中出现的总次数
注意:子串可能出现相同的情况,所以应该统计以每个结点结束的子串的个数。还有就是访问一个结点过后,应该将该结点的结束标记去掉,不然会出现重复加了很多次的情况。(还有每次结束后,记得把字典树销毁,可能爆内存)
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node //字典树结点的结构体
{
int id,num;
node *next[26];
node *fail; //失配指针
node()
{
id=-1; //在字符串结束的结点处记录该字符串id
num=0; //以该节点结束的字符串数量
fail=NULL;
memset(next,0,sizeof(next));
}
};
queue<node*>que;
void Insert(char *s,node *root,int id) //构建字典树
{
node *p=root;
for(int i=0;s[i];i++)
{
int x=s[i]-'a';
if(p->next[x]==NULL)
p->next[x]=new node();
p=p->next[x];
}
p->id=id;
p->num++;
}
void ac_automation(node *root) //添加fail指针
{
que.push(root);
while(!que.empty())
{
node *p=que.front();
que.pop();
node *tmp=NULL;
for(int i=0;i<26;i++)
{
if(p->next[i]!=NULL)
{
if(p==root)
p->next[i]->fail=root;
else
{
tmp=p->fail;
while(tmp!=NULL)
{
if(tmp->next[i]!=NULL)
{
p->next[i]->fail=tmp->next[i];
break;
}
tmp=tmp->fail;
}
if(tmp==NULL)
p->next[i]->fail=root;
}
que.push(p->next[i]);
}
}
}
}
int query(char *s,node *root)
{
int num=0;
bool flag=false;
node *p=root;
for(int i=0;s[i];i++)
{
int j=s[i]-'a';
while(p->next[j]==NULL&&p!=root) //没匹配上
p=p->fail;
p=p->next[j];
if(p==NULL)
p=root;
node *tmp=p;
while(tmp!=root)
{
if(tmp->id!=-1)
{
num+=tmp->num;
tmp->id=-1; //注意这里,不然有可能重复计算
}
tmp=tmp->fail;
}
}
return num;
}
void destroy(node *root)
{
if(root == NULL)
return;
for(int i = 0; i < 26; i++)
if(root -> next[i] != NULL)
destroy(root -> next[i]);
delete root;
}
int main()
{
int n,m,T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
node *root=new node();
char s[51];
for(int i=0;i<n;i++)
{
scanf("%s",s);
Insert(s,root,i);
}
while(!que.empty())
que.pop();
ac_automation(root);
char s1[1000001];
scanf("%s",s1);
printf("%d\n",query(s1,root));
destroy(root);
}
return 0;
}