病毒侵袭持续中 HDU - 3065 AC自动机 巨坑的题 CSND博客

我从来没有做过这么坑的题目,天坑啊啊啊啊~内心崩溃,从昨晚开始debug,早上概论课加中午,起码debug了的4小时,结果屁点bug没有,多组输入!!!坑死人不偿命的,多组数组你倒是在题目中说一下啊,我要抓狂了,题解后面详述。

病毒侵袭持续中 HDU - 3065

小t非常感谢大家帮忙解决了他的上一个问题。然而病毒侵袭持续中。在小t的不懈努力下,他发现了网路中的“万恶之源”。这是一个庞大的病毒网站,他有着好多好多的病毒,但是这个网站包含的病毒很奇怪,这些病毒的特征码很短,而且只包含“英文大写字符”。当然小t好想好想为民除害,但是小t从来不打没有准备的战争。知己知彼,百战不殆,小t首先要做的是知道这个病毒网站特征:包含多少不同的病毒,每种病毒出现了多少次。大家能再帮帮他吗?
Input
第一行,一个整数N(1<=N<=1000),表示病毒特征码的个数。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在1—50之间,并且只包含“英文大写字符”。任意两个病毒特征码,不会完全相同。
在这之后一行,表示“万恶之源”网站源码,源码字符串长度在2000000之内。字符串中字符都是ASCII码可见字符(不包括回车)。
Output
按以下格式每行一个,输出每个病毒出现次数。未出现的病毒不需要输出。
病毒特征码: 出现次数
冒号后有一个空格,按病毒特征码的输入顺序进行输出。
Sample Input
3
AA
BB
CC
ooxxCC%dAAAoen…END
Sample Output
AA: 2
CC: 1

Hint
Hit:
题目描述中没有被提及的所有情况都应该进行考虑。比如两个病毒特征码可能有相互包含或者有重叠的特征码段。
计数策略也可一定程度上从Sample中推测。

这道题目是典型的AC自动机的题目,先构造字典树,然后利用KMP算法的思路构建fail指针,最后是模板串的匹配,
这道题目没有说明是多组输入,但是不多组输入会WA,我已经被坑进去一上午了,屁点bug没有,从头到尾重新看了三遍,找到了一个错误还是WA,后来多组数据把没改错误的代码贴上去居然还过了,但是明显是错误的答案。那个错误在于每遍历一个模板字符的时候,都应该回溯一遍查找是否从开始到当前字符已经存在匹配到了病毒字符串,但是我开始写的是只有在匹配到某个病毒字符串的时候才回溯,结果显然不对,比如输入数据
4
AA
BB
CC
AABBCC
AABBCC
结果会是
AA:1
CC:1
AABBCC:1
结果是显然不对的,BB没有被输出,但是代码仍然accept了,这道题目的测评就有问题。简直就是毒瘤
下面是我改进后完全正确的代码:

#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2000000 + 5;
const int MAX = 1010*50;
const int kind=26;
char s[maxn];
struct node{
    node *next[26];
    node *fail;
    int id;
    node(){
    	fail=NULL;
    	id=0;
    	for(int i=0;i<kind;i++){
            this->next[i]=NULL; 
		}
    	
	}
}*q[MAX];
char mode[maxn];
char keyword[1010][55];
node *root=new node();
int cnt[1010];
void Insert(char *s,int id)
{
    node *p = root;
    for(int i=0;s[i]!='\0';i++){
    	int k=s[i]-'A';
    	if(p->next[k]==NULL){
    		p->next[k]=new node();
		}
		p=p->next[k];
	}
	p->id=id;
    
}
void build_fail_pointer()
{	
	int head=0,tail=0;
	node *p = root;
	q[tail++]=p;
	while(head!=tail){
		node *temp=q[head++];
		if(temp==root){
			for(int i=0;i<26;i++){
				if(temp->next[i]!=NULL){
					temp->next[i]->fail=root;
					q[tail++]=root->next[i];
				}
			}
		}
		else{
			for(int i=0;i<26;i++){
			    if(temp->next[i]!=NULL){
			    	node *r=temp->fail;
					while(r!=NULL){
                        if(r->next[i]!=NULL){
			                temp->next[i]->fail=r->next[i];
			                q[tail++]=temp->next[i];
			    			break;
					    }
					    r=r->fail;
					}
					if(r==NULL){
						temp->next[i]->fail=root;
						q[tail++]=temp->next[i];
					} 
				}		    
           }	
		}
	}   
}
void ac_automation()
{
    node *p = root;
    int len=strlen(mode);
    for(int i=0;i<len;i++){
    	if(mode[i]<'A'||mode[i]>'Z'){
        	p=root;
        	continue;
		} 
        int index = mode[i]-'A';
       	while(p!=root&&p->next[index]==NULL) p=p->fail;
       	p=p->next[index];
        if(p==NULL) p=root;	
		node *t=p; 
 	    while(t!=root&&t!=NULL){
 	    	if(t->id>0)
                cnt[t->id]++;
    		t=t->fail;
		} 	
    }
}
int main(){
	int n;
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++){
		scanf("%s",keyword[i]);
		Insert(keyword[i],i);
		cnt[i]=0;
	}
	build_fail_pointer();
	scanf("%s",mode);
	ac_automation();
	for(int i=1;i<=n;i++){
		if(cnt[i]!=0){
			printf("%s: %d\n",keyword[i],cnt[i]);
		}
	}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值