看毛片就能AC算法
KMP && ACA
KMP:
吼哇!
反正网上教程满天飞,我就不写了。
发个自己写的模板
1 /** 2 freopen("in.in", "r", stdin); 3 freopen("right.out", "w", stdout); 4 */ 5 //// ///////////////////////////// 6 #include <cstdio> 7 #include <string> 8 #include <iostream> 9 using std::string; 10 const int N = 1000010; 11 12 int nex[N]; 13 14 int main() { 15 freopen("in.in", "r", stdin); 16 freopen("right.out", "w", stdout); 17 string s, p; 18 std::cin >> s >> p; 19 20 nex[0] = 0; 21 for(int i = 1, j = 0; i < p.size(); i++) { 22 while(j && p[i] != p[j]) { 23 j = nex[j - 1]; 24 } 25 if(p[i] == p[j]) j++; 26 27 /// while(j && p[i + 1] == p[j]) j = nex[j - 1]; 28 /// 十分失败的优化,反例: 29 /// acccc 30 /// ccc 31 /// 001 :next 32 /// 可以看出能匹配两个串,但是这个优化只能算出一个来 33 34 nex[i] = j; 35 } 36 37 for(int i = 0, j = 0; i < s.size(); i++) { 38 while(j && s[i] != p[j]) { 39 j = nex[j - 1]; 40 } 41 if(s[i] == p[j]) j++; 42 if(j == p.size()) { 43 printf("%d\n", i - j + 2); 44 j = nex[j - 1]; 45 } 46 } 47 48 for(int i = 0; i < p.size(); i++) { 49 printf("%d ", nex[i]); 50 } 51 52 return 0; 53 }
例题:
AC自动机:
就是KMP上trie,用以解决多串匹配问题。
当我花几天的时间理解KMP之后,AC自动机也就很显然了(这怎么就显然了?)
想当我学KMP满大街找教程,现在学AC自动机教程还没看完就懂了...所以说基础很重要。
大致想象一下就行了。直接发代码吧。
1 #include <cstdio> 2 #include <string> 3 #include <iostream> 4 #include <queue> 5 #include <cstring> 6 const int N = 1000010; 7 8 using std::string; 9 10 struct AC { 11 int root, tot; 12 int tr[N][26], nex[N], ed[N]; 13 bool vis[N]; 14 AC() { 15 root = 1; 16 tot = 1; 17 } 18 void clear() { 19 for(int i = 1; i <= tot; i++) { 20 nex[i] = ed[i] = 0; 21 for(int j = 0; j < 26; j++) { 22 tr[i][j] = 0; 23 } 24 } 25 return; 26 } 27 28 inline void insert(string x) { 29 int p = root; 30 for(int i = 0; i < x.size(); i++) { 31 int f = x[i] - 'a'; 32 if(!tr[p][f]) { 33 tr[p][f] = ++tot; 34 } 35 p = tr[p][f]; 36 } 37 ed[p]++; 38 return; 39 } 40 void getnex() { 41 nex[root] = root; 42 std::queue<int> Q; 43 Q.push(root); 44 while(!Q.empty()) { 45 int x = Q.front(); 46 Q.pop(); 47 for(int i = 0; i < 26; i++) { 48 int y = tr[x][i]; 49 if(y) { 50 int j = nex[x]; 51 while(j != root && !tr[j][i]) { 52 j = nex[j]; 53 } 54 if(tr[j][i] && x != root) { 55 j = tr[j][i]; 56 } 57 nex[y] = j; 58 Q.push(y); 59 } 60 } 61 } 62 return; 63 } 64 int solve(string x) { 65 int ans = 0; 66 memset(vis, 0, (tot + 1) * sizeof(bool)); 67 for(int i = 0, j = root; i < x.size(); i++) { 68 int f = x[i] - 'a'; 69 while(j != root && !tr[j][f]) { 70 j = nex[j]; 71 } 72 if(tr[j][f]) { 73 j = tr[j][f]; 74 } 75 if(ed[j] && !vis[j]) { 76 ans += ed[j]; 77 vis[j] = 1; 78 } 79 } 80 return ans; 81 } 82 }ac; 83 84 int main() { 85 int n; 86 scanf("%d", &n); 87 string s; 88 for(int i = 1; i <= n; i++) { 89 std::cin >> s; 90 ac.insert(s); 91 } 92 ac.getnex(); 93 std::cin >> s; 94 printf("%d", ac.solve(s)); 95 return 0; 96 }
1 #include <cstdio> 2 #include <string> 3 #include <iostream> 4 #include <queue> 5 #include <cstring> 6 const int N = 1000010; 7 8 using std::string; 9 10 struct Ans { 11 string s; 12 int cnt; 13 }a[N]; 14 15 struct AC { 16 int root, tot; 17 int tr[N][26], nex[N], ed[N]; 18 AC() { 19 root = 1; 20 tot = 1; 21 } 22 void clear() { 23 for(int i = 1; i <= tot; i++) { 24 nex[i] = ed[i] = 0; 25 for(int j = 0; j < 26; j++) { 26 tr[i][j] = 0; 27 } 28 } 29 tot = 1; 30 return; 31 } 32 33 inline void insert(string x, int k) { 34 int p = root; 35 for(int i = 0; i < x.size(); i++) { 36 int f = x[i] - 'a'; 37 if(!tr[p][f]) { 38 tr[p][f] = ++tot; 39 } 40 p = tr[p][f]; 41 } 42 ed[p] = k; 43 return; 44 } 45 void getnex() { 46 nex[root] = root; 47 std::queue<int> Q; 48 Q.push(root); 49 while(!Q.empty()) { 50 int x = Q.front(); 51 Q.pop(); 52 for(int i = 0; i < 26; i++) { 53 int y = tr[x][i]; 54 if(y) { 55 int j = nex[x]; 56 while(j != root && !tr[j][i]) { 57 j = nex[j]; 58 } 59 if(tr[j][i] && x != root) { 60 j = tr[j][i]; 61 } 62 nex[y] = j; 63 Q.push(y); 64 } 65 } 66 } 67 return; 68 } 69 void solve(string x) { 70 for(int i = 0, j = root; i < x.size(); i++) { 71 int f = x[i] - 'a'; 72 while(j != root && !tr[j][f]) { 73 j = nex[j]; 74 } 75 if(tr[j][f]) { 76 j = tr[j][f]; 77 } 78 int jj = j; 79 while(jj != root) { 80 if(ed[jj]) { 81 a[ed[jj]].cnt++; 82 } 83 jj = nex[jj]; 84 } 85 } 86 return; 87 } 88 }ac; 89 90 int main() { 91 int n; 92 string x; 93 while(scanf("%d", &n) && n) { 94 ac.clear(); 95 for(int i = 1; i <= n; i++) { 96 std::cin >> a[i].s; 97 ac.insert(a[i].s, i); 98 } 99 ac.getnex(); 100 std::cin >> x; 101 ac.solve(x); 102 int large = -1; 103 for(int i = 1; i <= n; i++) { 104 large = std::max(large, a[i].cnt); 105 } 106 printf("%d\n", large); 107 for(int i = 1; i <= n; i++) { 108 if(a[i].cnt == large) { 109 std::cout << a[i].s << std::endl; 110 } 111 } 112 for(int i = 1; i <= n; i++) { 113 a[i].cnt = 0; 114 } 115 } 116 return 0; 117 }
重点在于getnex()函数。别的依题意稍作修改即可。
第二题有个优化:ed[nex[jj]]可能为0,会额外增加跳的次数。解决办法是搞一个g数组出来表示ed[]不为0的某个nex。
被之前失败的优化经历搞怕了,没用...
KMP与AC自动机的小差异:(在我的模板中)
KMP初始化的nex[0] = 0,而AC自动机的初始化nex[root] = root;
KMP中的nex[i]表示最长匹配数,是比下标多1的,而AC自动机中直接指向一个节点。