病毒侵袭持续中 HDU - 3065 AC自动机
小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中推测。
题解:
和病毒侵袭 HDU - 2896 差不多
注意:
1:gets读入
2:由于目标串中匹配的时候有很多字符,但是你又不能去掉他们,但是next数组开到127的话太消耗内存,我的做法除了 'A'-'Z' 的字符之外的所有字母都变成 'Z'+1 ,这样的话只要开 next[27] 就够了。
1 /* 2 代码中: 3 叶节点:代表此节点下没有子节点 4 根节点:就是树的根 5 子节点:就是这个节点的直接相连的节点(直系节点) 6 7 此代码: 8 用题目所给模式串构成一颗字典树 9 然后找出来给出的待求串中每种模式串出现 10 11 */ 12 #include<stdio.h> 13 #include<iostream> 14 #include<string.h> 15 #include<algorithm> 16 #include<queue> 17 using namespace std; 18 const int maxn=5e4+10; 19 const int N=28; 20 typedef long long ll; 21 int v[1005]; 22 char ss[1005][55]; 23 struct Trie 24 { 25 int next[maxn][N],fail[maxn],ends[maxn]; 26 int root,L; 27 int New_node() //创建一个新节点 28 { 29 for(int i=0; i<N; ++i) 30 { 31 next[L][i]=-1; 32 } 33 ends[L++]=0; 34 return L-1; 35 } 36 void init() //创建根节点 37 { 38 L=0; 39 root=New_node(); 40 } 41 void inserts(char s[],int x) //往字典树里面插入新字符串 42 { 43 int len=strlen(s); 44 int now=root; 45 for(int i=0; i<len; ++i) 46 { 47 if(next[now][s[i]-'A']==-1) 48 next[now][s[i]-'A']=New_node(); 49 now=next[now][s[i]-'A']; 50 } 51 ends[now]=x; 52 } 53 void build() 54 { 55 queue<int>r; 56 fail[root]=root; 57 for(int i=0; i<N; ++i) 58 { 59 if(next[root][i]==-1) 60 { 61 next[root][i]=root; 62 } 63 else 64 { 65 fail[next[root][i]]=root; 66 r.push(next[root][i]); 67 } 68 } 69 while(!r.empty()) 70 { 71 int now=r.front(); 72 r.pop(); 73 for(int i=0; i<N; ++i) 74 { 75 if(next[now][i]==-1) 76 { 77 next[now][i]=next[fail[now]][i]; //叶节点处没有设置失败节点而是往next上接了一段,这个时候 78 //这个fail里面的值已经在下面的else里面放过东西了 79 } 80 else 81 { 82 fail[next[now][i]]=next[fail[now]][i]; //此节点的子节点的失败节点就是此节点失败节点对应字符位置 83 r.push(next[now][i]); 84 } 85 } 86 } 87 } 88 void query(char s[]) 89 { 90 int len=strlen(s); 91 int now=root; 92 //int res=0; 93 for(int i=0; i<len; ++i) 94 { 95 now=next[now][s[i]-'A']; //这个now就保证了我们找的肯定是这个字符串中的一部分 96 int temp=now; //这就是一直找,找到了就加1,找不到就通过失败节点看其他地方能不能找到 97 while(temp!=root) 98 { 99 if(ends[temp]) 100 { 101 v[ends[temp]]++; 102 } 103 //res+=ends[temp]; 104 //ends[temp]=0; 105 temp=fail[temp]; 106 } 107 } 108 //return res; 109 } 110 }; 111 char s[2000010]; 112 Trie ac; 113 int main() 114 { 115 // int t; 116 // scanf("%d",&t); 117 // while(t--) 118 // { 119 int n; 120 while(~scanf("%d",&n)) 121 { 122 memset(v,0,sizeof(v)); 123 ac.init(); 124 for(int j=1; j<=n; ++j) 125 { 126 scanf("%s",ss[j]); 127 int len=strlen(ss[j]); 128 for(int i=0; i<len; ++i) 129 { 130 if(ss[j][i]>'Z' || ss[j][i]<'A') 131 { 132 ss[j][i]='A'+27; 133 } 134 } 135 ac.inserts(ss[j],j); 136 } 137 getchar(); 138 ac.build(); 139 gets(s); 140 int len=strlen(s); 141 for(int i=0; i<len; ++i) 142 { 143 if(s[i]>'Z' || s[i]<'A') 144 { 145 s[i]='A'+27; 146 } 147 } 148 //printf("**\n"); 149 ac.query(s); 150 //printf("*"); 151 for(int i=1; i<=n; ++i) 152 { 153 if(v[i]) 154 { 155 printf("%s: %d\n",ss[i],v[i]); 156 } 157 } 158 } 159 160 // } 161 return 0; 162 }