病毒侵袭 HDU - 2896 板子题
当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻。。。。在这样的时刻,人们却异常兴奋——我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事儿啊~~
但网路上总有那么些网站,开始借着民众的好奇心,打着介绍日食的旗号,大肆传播病毒。小t不幸成为受害者之一。小t如此生气,他决定要把世界上所有带病毒的网站都找出来。当然,谁都知道这是不可能的。小t却执意要完成这不能的任务,他说:“子子孙孙无穷匮也!”(愚公后继有人了)。
万事开头难,小t收集了好多病毒的特征码,又收集了一批诡异网站的源码,他想知道这些网站中哪些是有病毒的,又是带了怎样的病毒呢?顺便还想知道他到底收集了多少带病毒的网站。这时候他却不知道何从下手了。所以想请大家帮帮忙。小t又是个急性子哦,所以解决问题越快越好哦~~
但网路上总有那么些网站,开始借着民众的好奇心,打着介绍日食的旗号,大肆传播病毒。小t不幸成为受害者之一。小t如此生气,他决定要把世界上所有带病毒的网站都找出来。当然,谁都知道这是不可能的。小t却执意要完成这不能的任务,他说:“子子孙孙无穷匮也!”(愚公后继有人了)。
万事开头难,小t收集了好多病毒的特征码,又收集了一批诡异网站的源码,他想知道这些网站中哪些是有病毒的,又是带了怎样的病毒呢?顺便还想知道他到底收集了多少带病毒的网站。这时候他却不知道何从下手了。所以想请大家帮帮忙。小t又是个急性子哦,所以解决问题越快越好哦~~
Input第一行,一个整数N(1<=N<=500),表示病毒特征码的个数。
接下来N行,每行表示一个病毒特征码,特征码字符串长度在20—200之间。
每个病毒都有一个编号,依此为1—N。
不同编号的病毒特征码不会相同。
在这之后一行,有一个整数M(1<=M<=1000),表示网站数。
接下来M行,每行表示一个网站源码,源码字符串长度在7000—10000之间。
每个网站都有一个编号,依此为1—M。
以上字符串中字符都是ASCII码可见字符(不包括回车)。
Output依次按如下格式输出按网站编号从小到大输出,带病毒的网站编号和包含病毒编号,每行一个含毒网站信息。
web 网站编号: 病毒编号 病毒编号 …
冒号后有一个空格,病毒编号按从小到大排列,两个病毒编号之间用一个空格隔开,如果一个网站包含病毒,病毒数不会超过3个。
最后一行输出统计信息,如下格式
total: 带病毒网站数
冒号后有一个空格。
Sample Input
3 aaa bbb ccc 2 aaabbbccc bbaacc
Sample Output
web 1: 1 2 3 total: 1
题解:
就跑一遍AC自动机代码就完了,比起原板子题Keywords Search HDU - 2222就改了一点
AC自动机代码详解看Keywords Search HDU - 2222
代码:
1 /* 2 代码中: 3 叶节点:代表此节点下没有子节点 4 根节点:就是树的根 5 子节点:就是这个节点的直接相连的节点(直系节点) 6 7 此代码: 8 用题目所给模式串构成一颗字典树 9 然后找出来给出的待求串中每种模式串出现几次 10 11 12 1.题目中的字符是除去回车的ASCII码可见字符(95个),我最开始定义的是以全部的ASCII码的个数(128)为基准的 13 2.输出要排序后再输出 14 */ 15 #include<stdio.h> 16 #include<iostream> 17 #include<string.h> 18 #include<algorithm> 19 #include<queue> 20 using namespace std; 21 const int maxn=30000000; 22 typedef long long ll; 23 int visit[maxn],length[1005]; 24 int v[1005][1005],n,m; 25 struct Trie 26 { 27 int next[maxn/100][128],fail[maxn],ends[maxn]; 28 int root,L; 29 int New_node() //创建一个新节点 30 { 31 for(int i=0; i<128; ++i) 32 { 33 next[L][i]=-1; 34 } 35 ends[L++]=0; 36 return L-1; 37 } 38 void init() //创建根节点 39 { 40 L=0; 41 root=New_node(); 42 } 43 void inserts(char s[],int x) //往字典树里面插入新字符串 44 { 45 int len=strlen(s); 46 int now=root; 47 for(int i=0; i<len; ++i) 48 { 49 if(next[now][s[i]]==-1) 50 next[now][s[i]]=New_node(); 51 now=next[now][s[i]]; 52 } 53 ends[now]=x; 54 } 55 void build() 56 { 57 queue<int>r; 58 fail[root]=root; 59 for(int i=0; i<128; ++i) 60 { 61 if(next[root][i]==-1) 62 { 63 next[root][i]=root; 64 } 65 else 66 { 67 fail[next[root][i]]=root; 68 r.push(next[root][i]); 69 } 70 } 71 while(!r.empty()) 72 { 73 int now=r.front(); 74 r.pop(); 75 for(int i=0; i<128; ++i) 76 { 77 if(next[now][i]==-1) 78 { 79 next[now][i]=next[fail[now]][i]; //叶节点处没有设置失败节点而是往next上接了一段,这个时候 80 //这个fail里面的值已经在下面的else里面放过东西了 81 } 82 else 83 { 84 fail[next[now][i]]=next[fail[now]][i]; //此节点的子节点的失败节点就是此节点失败节点对应字符位置 85 r.push(next[now][i]); 86 } 87 } 88 } 89 } 90 void query(char s[],int x) 91 { 92 int len=strlen(s); 93 int now=root; 94 for(int i=0; i<len; ++i) 95 { 96 now=next[now][s[i]]; //这个now就保证了我们找的肯定是这个字符串中的一部分 97 int temp=now; //这就是一直找,找到了就加1,找不到就通过失败节点看其他地方能不能找到 98 while(temp!=root) 99 { 100 if(ends[temp] && !visit[temp]) 101 { 102 visit[temp]=1; 103 v[x][length[x]++]=ends[temp]; 104 } 105 temp=fail[temp]; 106 } 107 } 108 } 109 }; 110 char s[maxn]; 111 Trie ac; 112 void init() 113 { 114 for(int i=0; i<=ac.L; ++i) 115 { 116 visit[i]=0; 117 } 118 for(int i=1; i<=n; ++i) 119 length[i]=0; 120 } 121 122 int main() 123 { 124 scanf("%d",&n); 125 ac.init(); 126 for(int i=1; i<=n; ++i) 127 { 128 scanf("%s",s); 129 ac.inserts(s,i); 130 } 131 scanf("%d",&m); 132 ac.build(); 133 int ans=0; 134 for(int i=1; i<=m; ++i) 135 { 136 init(); 137 scanf("%s",s); 138 ac.query(s,i); 139 if(length[i]) 140 { 141 ans++; 142 printf("web %d: ",i); 143 sort(v[i],v[i]+length[i]); //之前是忘记加这个一直错,想的时候还知道要排序,写代码的时候就忘了 144 for(int j=0; j<length[i]; ++j) 145 { 146 if(j!=length[i]-1) printf("%d ",v[i][j]); 147 else printf("%d\n",v[i][j]); 148 } 149 } 150 } 151 printf("total: %d\n",ans); 152 return 0; 153 }