http://acm.hdu.edu.cn/showproblem.php?pid=3065
题意:多模式匹配,分别求出每个单词在字符串中出现的个数,字符串中有各种ASCII码可见字符,单词中只有大小写字母。
在模板的基础上统计一下各个单词出现的次数,在node中加一个记录以这个节点结尾的单词的编号的id变量,在匹配成功的时候相应地累加到ans数组中即可。
一开始MLE了好几发,因为这里字典树中的字符都是大小写字母所以不要开ASCII码表中所有字符数那么大,只需要开53即可(存大小写字母各26个,其余字符都不会存在于单词中,都放到第53(下标52)),开到100多就会MLE。
#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=2000010;
struct node{
node *next[53];//子节点指针数组,开53是因为要存大小写字母各26个,其余字符都不会存在于单词中,都放到第53(下标52)
node *fail;//失配指针
int sum,id;//sum记录这个节点是不是一个单词的结尾及相应个数,id记录这是哪一个单词
node()
{
for(int j=0;j<53;j++) next[j]=NULL;
sum=0,id=0,fail=NULL;
}
~node()
{
for(int j=0;j<53;j++)
{
if(next[j]!=NULL)
delete next[j];
}
}
};
int cnt;
int ans[1010];
node* root;
node* newnode;
char key[1010][55];
char pattern[maxn];
//建立字典树
void Insert(char *s,int c)
{
node* p=root;
for(int i=0;s[i];i++)
{
int x=s[i];
if(s[i]>='a'&&s[i]<='z') x=s[i]-'a';
else if(s[i]>='A'&&s[i]<='Z') x=s[i]-'A'+26;
if(p->next[x]==NULL)
{
newnode=new node();
p->next[x]=newnode;
}
p=p->next[x];
}
p->sum++;
p->id=c;
}
//求解每个节点的失配指针
void getfail()
{
queue<node*> q;
q.push(root);
node* p;
node* tmp;
while(!q.empty())
{
tmp=q.front();
q.pop();
for(int i=0;i<53;i++)
{
if(tmp->next[i])
{
if(tmp==root)
tmp->next[i]->fail=root;
else
{
p=tmp->fail;
while(p)
{
if(p->next[i])
{
tmp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL) tmp->next[i]->fail=root;
}
q.push(tmp->next[i]);
}
}
}
}
//匹配
void ac_automation(char *ch)
{
node* p=root;
int len=strlen(ch);
for(int i=0;i<len;i++)
{
int x;
if(ch[i]>='a'&&ch[i]<='z') x=ch[i]-'a';
else if(ch[i]>='A'&&ch[i]<='Z') x=ch[i]-'A'+26;
else x=52;//其余字符都当成不会被匹配的52
while(!p->next[x]&&p!=root) p=p->fail;
p=p->next[x];
if(!p) p=root;
node* tmp=p;
while(tmp!=root)
{
if(tmp->sum>=0)
{
cnt+=tmp->sum;
ans[tmp->id]+=tmp->sum;
//tmp->sum=-1;//注释掉这里以继续匹配之前匹配过的单词
}
else break;
tmp=tmp->fail;
}
}
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(ans,0,sizeof(ans));
root=new node();
for(int i=1;i<=n;i++)
{
scanf("%s",key[i]);
Insert(key[i],i);
}
scanf("%s",pattern);
cnt=0;
getfail();
ac_automation(pattern);
for(int i=1;i<=n;i++)
{
if(ans[i]>0)
printf("%s: %d\n",key[i],ans[i]);
}
delete root;//每次都要delete不然会MLE
}
return 0;
}