题目
给一些模式串,一个标准串
统计每个串的出现次数
注意模式串中只有大写字母
标准串可见字符啥都有
思路来源
https://www.cnblogs.com/gongxijun/p/4018255.html
https://www.cnblogs.com/Simon-X/p/5687318.html
心得
开始忘了memsetTrie树,然后疯狂tle???
卧槽我该剪枝的都减了啊……
其实就一处,<'A'或>'Z'的直接回root就行了,
毕竟这种情况下按下标访问是会越界的……
然后发现了,把最开始的代码一改,A了...
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
int id,fail,Next[26],sum;
}trie[50005];
char s[1005][55],web[2000005];
int que[50005];//建ac自动机的队列
int root,tot;//根的编号 节点编号
bool ok[1005];//i病毒是否出现
int t,n,res[1005],ans[1005],cnt;
void insert(int r,char *s,int id)//(root,串,串号),trie树插入
{
int len=strlen(s);
for(int i=0;i<len;i++)
{
if(trie[r].Next[s[i]-65]==0)
trie[r].Next[s[i]-65]=++tot;
r=trie[r].Next[s[i]-65];
}
trie[r].id=id;
}
void build(int r)//建ac自动机
{
int head=0,tail=0;
trie[r].fail=r;//root自己连自己
que[tail++]=r;
while(head<tail)
{
r=que[head++];
for(int i=0;i<26;i++)
{
int ch=trie[r].Next[i],failp;
if(ch)
{
que[tail++]=ch;
for(failp=trie[r].fail;failp!=root&&trie[failp].Next[i]==0;failp=trie[failp].fail);//向前回跳
if(trie[failp].Next[i]==0||trie[failp].Next[i]==ch)trie[ch].fail=failp;//接到根或者自己
else trie[ch].fail=trie[failp].Next[i];//可以后接
}
}
}
}
void query(int r,char *s)
{
int len=strlen(s);
for(int i=0;i<len;i++)
{
if(s[i]<'A'||s[i]>'Z')//暴力剪枝
{
r=root;
continue;
}
while(trie[r].Next[s[i]-65]==0&&r!=root)r=trie[r].fail;
r=trie[r].Next[s[i]-65];//可以向后走就向后走,从root向后走也行,否则就是root;
if(!r)r=root;
for(int tempfail=r;tempfail!=root;tempfail=trie[tempfail].fail)//对自动机上的一段类似KMP
{
if(trie[tempfail].id>0)//暴力统计好了 搞一个内部子串的缀和貌似也挺麻烦的
{
if(!res[trie[tempfail].id])ans[cnt++]=trie[tempfail].id;
res[trie[tempfail].id]++;
}
}
}
}
void init()
{
tot=cnt=0;
root=++tot;
memset(res,0,sizeof res);
memset(trie,0,sizeof trie);
}
int main()
{
while(~scanf("%d",&n))
{
init();
for(int i=0;i<n;i++)
{
scanf("%s",s[i]);
insert(root,s[i],i+1);
}
build(root);
scanf("%s",web);
query(root,web);
sort(ans,ans+cnt);
for(int i=0;i<cnt;++i)
{
printf("%s: %d\n",s[ans[i]-1],res[ans[i]]);
}
}
return 0;
}