题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896
这个在模板的基础之上增加一个标记就可以了,保存每一个出现的病毒就可以了;所以啊,代码里的cnt是一个变化很灵活的变量;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
const int N=130; // ASCII码可见字符0~127;
char str[10005];
int vis[555];
struct node{
int cnt;
node *fail;
node *next[N];
node(){
cnt=0;
fail=NULL;
memset(next,0,sizeof(next));
}
};
node *p,*temp,*head;
void Insert(char s[],int num) // 第一步,构造字典树;
{
p=head;
int i=0,id;
while(s[i]){
id=s[i++]-' ';
if(p->next[id]==NULL) p->next[id]=new node();
p=p->next[id];
}
p->cnt=num; // 保存这是第几个病毒;
}
void get_fail() // 构造fail指针;
{
node *son;
p=head;
queue<struct node *>q;
q.push(p);
while(!q.empty()){ // 广搜,构造所有的fail指针;
temp=q.front();
q.pop();
for(int i=0;i<128;i++){ // 遍历所有可能的子节点;
son=temp->next[i];
if(son!=NULL){ // 子节点不为空时;
if(temp==head) son->fail=head; // 当子节点为第二层节点时;
else{
p=temp->fail; // 当不为前两层节点时;
while(p){
if(p->next[i]){ // 如果父节点的fail指针的下一层存在,那么改子节点的fail便是指向那里;
son->fail=p->next[i];
break; // 构造成功,便可以直接退出;
}
p=p->fail;
}
if(!p) son->fail=head; // 如果没有构造成功,那么fail便只有指向head了;
}
q.push(son); // 新元素入队;
}
}
}
}
int Query() // 查询;
{
int i=0,ans=0;
p=head;
while(str[i]){
int id=str[i++]-' ';
while(p->next[id]==NULL&&p!=head) p=p->fail; // 这个字符的下一个不存在,且不是第一层字符节点;
p=p->next[id]; // 将p更新到该有的位置;
if(!p) p=head; // 如果不存在;
temp=p;
while(temp!=head){
vis[temp->cnt]=1; // 标记每一个出现的病毒;
if(temp->cnt!=0) ans++;
temp=temp->fail;
}
}
return ans;
}
int main()
{
int n,m;
head=new node();
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++){ // 构造字典树;
scanf("%s",str);
Insert(str,i);
}
get_fail(); // 构造fail指针;
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%s",str);
memset(vis,0,sizeof(vis));
int tmp=Query();
if(tmp){
ans++;
printf("web %d:",i);
for(int j=1;j<=n;j++)
if(vis[j]) printf(" %d",j);
printf("\n");
}
}
printf("total: %d\n",ans);
return 0;
}