hdu3065 病毒侵袭持续中(AC自动机模板题)

题目

给一些模式串,一个标准串

统计每个串的出现次数

注意模式串中只有大写字母

标准串可见字符啥都有

思路来源

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值