1、uva 1449/LA 4670 Dominating Patterns
题意:有n个模板串和一个文本串,找出哪些模板串在文本串中出现次数最多。
思路:AC自动机模板

1 #include<cstring> 2 #include<queue> 3 #include<cstdio> 4 #include<map> 5 #include<string> 6 using namespace std; 7 8 const int SIGMA_SIZE = 26; 9 const int MAXNODE = 11000; 10 const int MAXS = 150 + 10; 11 12 map<string, int> ms; 13 14 struct ACTree 15 { 16 int ch[MAXNODE][SIGMA_SIZE]; 17 int f[MAXNODE]; // fail函数 18 int val[MAXNODE]; // 每个字符串的结尾结点都有一个非0的val 19 int last[MAXNODE]; // last[j]表示结点j沿着失配指针往回走时,遇到的下一个单词结点编号(后缀链接) 20 int cnt[MAXS]; 21 int sz; 22 23 void init() 24 { 25 sz = 1; 26 memset(ch[0], 0, sizeof(ch[0])); 27 memset(cnt, 0, sizeof(cnt)); 28 ms.clear(); 29 } 30 31 // 字符c的编号 32 int idx(char c) 33 { 34 return c - 'a'; 35 } 36 37 // 插入字符串。v必须非0 38 void insert(char *s, int v) 39 { 40 int u = 0, n = strlen(s); 41 for (int i = 0; i < n; i++) 42 { 43 int c = idx(s[i]); 44 if (!ch[u][c]) 45 { 46 memset(ch[sz], 0, sizeof(ch[sz])); 47 val[sz] = 0; 48 ch[u][c] = sz++; 49 } 50 u = ch[u][c]; 51 } 52 val[u] = v; 53 ms[string(s)] = v; 54 } 55 56 //统计以结点j结尾的所有字符串 57 void Cnt(int j) 58 { 59 if (j) 60 { 61 cnt[val[j]]++; 62 Cnt(last[j]); 63 } 64 } 65 66 // 在T中找模板 67 int find(char* T) 68 { 69 int n = strlen(T); 70 int j = 0; // 当前结点编号,初始为根结点 71 for (int i = 0; i < n; i++) 72 { // 文本串当前指针 73 int c = idx(T[i]); 74 //while (j && !ch[j][c]) j = f[j]; // 顺着失配边走,直到可以匹配 75 j = ch[j][c]; 76 if (val[j]) Cnt(j); 77 else if (last[j]) Cnt(last[j]); // 找到了 78 } 79 } 80 81 // 计算fail函数 82 void getFail() 83 { 84 queue<int> q; 85 f[0] = 0; 86 // 初始化队列 87 for (int c = 0; c < SIGMA_SIZE; c++) 88 { 89 int u = ch[0][c]; 90 if (u) 91 { 92 f[u] = 0; q.push(u); last[u] = 0; 93 } 94 } 95 // 按BFS顺序计算fail 96 while (!q.empty()) 97 { 98 int r = q.front(); q.pop(); 99 for (int c = 0; c < SIGMA_SIZE; c++) 100 { 101 int u = ch[r][c]; 102 //if (!u) continue; 103 if (!u) 104 { 105 ch[r][c] = ch[f[r]][c]; 106 continue; 107 } 108 q.push(u); 109 int v = f[r]; 110 while (v && !ch[v][c]) v = f[v]; 111 f[u] = ch[v][c]; 112 last[u] = val[f[u]] ? f[u] : last[f[u]]; 113 } 114 } 115 } 116 117 }; 118 119 ACTree ac; 120 char text[1000001], P[151][80]; 121 int n, T; 122 123 int main() 124 { 125 while (scanf("%d", &n) == 1 && n) 126 { 127 ac.init(); 128 for (int i = 1; i <= n; i++) 129 { 130 scanf("%s", P[i]); 131 ac.insert(P[i], i); 132 } 133 ac.getFail(); 134 scanf("%s",text); 135 ac.find(text); 136 int best = -1; 137 for (int i = 1; i <= n; i++) 138 if (ac.cnt[i] > best) best = ac.cnt[i]; 139 printf("%d ", best); 140 for (int i = 1; i <= n; i++) 141 if (ac.cnt[ms[string(P[i])]] == best) printf("%s ", P[i]); 142 //不用ac.cnt[i]则是考虑模板串重复的情况,重复时后一个串会覆盖前一个串 143 } 144 return 0; 145 }
2、uva 11468 Substring
题意:给定一些模式串,然后给出一些字母出现的概率,每次随机出现一个字母,要求出这些字母出现L个组成的字符串不包含(即不是它的连续字串)已有模式串的概率
思路:先构造出AC自动机,构造的时候利用fail数组的特性,记录下每个位置是否有经过一个单词结点,如果有这个结点就是不能走的结点,那么问题就变成了只能在能走的结点上走L步的概率,然后进行dp即可。

1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 using namespace std; 6 7 const int MAXNODE = 405;// 结点总数 8 const int SIGMA_SIZE = 62;//大小写字母、数字 9 10 struct ACTree 11 { 12 13 int ch[MAXNODE][SIGMA_SIZE]; 14 int val[MAXNODE];//val[i]表示结点i为字符串的结尾或后缀为单词 15 int Fail[MAXNODE]; 16 int sz; 17 18 double dp[MAXNODE][105]; 19 double p[SIGMA_SIZE];//存概率 20 bool vis[MAXNODE][105]; 21 22 void init() 23 { 24 sz = 1; 25 memset(ch[0], 0, sizeof(ch[0])); 26 val[0] = 0; 27 28 memset(p, 0, sizeof(p)); 29 memset(vis, false, sizeof(vis)); 30 } 31 32 int idx(char c) 33 { 34 if (c >= 'a' && c <= 'z') return c - 'a'; 35 if (c >= 'A' && c <= 'Z') return c - 'A' + 26; 36 if (c >= '0' && c <= '9') return c - '0' + 52; 37 } 38 39 void insert(char *s) 40 { 41 int n = strlen(s); 42 int u = 0; 43 for (int i = 0; i < n; i++) 44 { 45 int c = idx(s[i]); 46 if (!ch[u][c]) 47 { 48 val[sz] = 0; 49 memset(ch[sz], 0, sizeof(ch[sz])); 50 ch[u][c] = sz++; 51 } 52 u = ch[u][c]; 53 } 54 val[u] = 1; 55 } 56 57 void getfail() 58 { 59 queue<int> Q; 60 Fail[0] = 0; 61 for (int c = 0; c < SIGMA_SIZE; c++) 62 { 63 int u = ch[0][c]; 64 if (u) 65 { 66 Fail[u] = 0; Q.push(u); 67 } 68 } 69 while (!Q.empty()) 70 { 71 int r = Q.front(); Q.pop(); 72 for (int c = 0; c < SIGMA_SIZE; c++) 73 { 74 int u = ch[r][c]; 75 if (!u) 76 { 77 ch[r][c] = ch[Fail[r]][c]; 78 continue; 79 } 80 Q.push(u); 81 int v = Fail[r]; 82 while (v && !ch[v][c]) v = Fail[v]; 83 Fail[u] = ch[v][c]; 84 val[u] |= val[Fail[u]];//注意有后缀为单词的情况 85 } 86 } 87 } 88 89 double solve(int u, int L) 90 { 91 if (vis[u][L]) return dp[u][L]; 92 vis[u][L] = true; 93 if (!L) return dp[u][L] = 1; 94 dp[u][L] = 0; 95 for (int c = 0; c < SIGMA_SIZE; c++) 96 { 97 if (val[ch[u][c]]) continue;//如果是单词结尾或后缀是单词,跳过 98 dp[u][L] += p[c] * solve(ch[u][c], L - 1); 99 } 100 return dp[u][L]; 101 } 102 }; 103 104 ACTree ac; 105 106 int t, n, L; 107 char str[25]; 108 109 int main() 110 { 111 int Case = 0; 112 scanf("%d", &t); 113 while (t--) 114 { 115 ac.init(); 116 scanf("%d", &n); 117 while (n--) 118 { 119 scanf("%s", str); 120 ac.insert(str); 121 } 122 ac.getfail(); 123 scanf("%d", &n); 124 while (n--) 125 { 126 scanf("%s", str); 127 scanf("%lf", &ac.p[ac.idx(str[0])]); 128 } 129 scanf("%d", &L); 130 printf("Case #%d: %.6lf ", ++Case, ac.solve(0, L)); 131 } 132 return 0; 133 }
3、uva 11019 Matrix Matcher
题意:给出一个n*m的字符矩阵T,找出给定的x*y的字符矩阵P在T中出现了多少次.
思路:
方法是利用count[i][j]来记录以(i,j)为左上角,且大小为x,y的矩形中含有多少个完整行与P矩阵对应行完全相同。
所以,如果在T矩阵的第r行的第c列开始与P的第i行匹配,那么count[r-i][c]++;
最后判断是否有count[][]==X.

1 //假设N*M矩阵为T,X*Y矩阵为P 2 //方法是利用count[i][j]来记录以(i, j)为左上角,且大小为X,Y的矩形中含有多少个完整行与P矩阵对应行完全相同。 3 //所以,如果在T矩阵的第r行的第c列开始与P的第i行匹配,那么count[r - i][c]++; 4 //最后判断是否有count[][] == X; 5 6 #include<cstring> 7 #include<cstdio> 8 #include<queue> 9 #include<iostream> 10 using namespace std; 11 const int maxn = 100 * 100 + 10; 12 const int sigma_size=26; 13 14 int N, M, X, Y; 15 char map[1005][1005]; 16 int cnt[1005][1005]; 17 18 struct AC_automation 19 { 20 struct node 21 { 22 int next[sigma_size]; 23 int val;//树节点的附加信息 24 int fail;//存储节点的失配指针 25 vector<int>line;//记录该单词所在的行数,可能多个行的单词相等 26 void init() 27 { 28 memset(next, 0, sizeof(next)); 29 line.clear(); 30 fail = val = 0; 31 } 32 }ch[maxn];//用来存储Trie树 33 int sz;//整棵树节点的总数 34 int idx(char c) 35 { 36 return c - 'a'; //对于小写字母集,获得c的编号 37 } 38 39 void init() 40 { 41 sz = 1; 42 ch[0].init(); 43 } 44 void insert(char *s, int v) 45 { 46 int u = 0, len = strlen(s); 47 for (int i = 0; i < len; i++) 48 { 49 int c = idx(s[i]); 50 if (ch[u].next[c] == 0) 51 { 52 ch[sz].init(); 53 ch[u].next[c] = sz++; 54 } 55 u = ch[u].next[c]; 56 } 57 ch[u].val = 1; 58 ch[u].line.push_back(v); 59 } 60 61 //利用bfs得到失配指针fail 62 void get_fail() 63 { 64 queue<int>Q; 65 for (int i = 0; i < sigma_size; i++) 66 if (ch[0].next[i] != 0) 67 Q.push(ch[0].next[i]); 68 while (!Q.empty()) 69 { 70 int temp = Q.front(); 71 Q.pop(); 72 for (int i = 0; i < sigma_size; i++) 73 { 74 if (ch[temp].next[i] != 0) 75 { 76 Q.push(ch[temp].next[i]); 77 int fail_temp = ch[temp].fail; 78 //关键步骤,如果fail指针所指的节点没有('a'+i)这个子节点,继续递归,直到fail_temp指向0节点,即根节点 79 while (fail_temp > 0 && ch[fail_temp].next[i] == 0) 80 fail_temp = ch[fail_temp].fail; 81 82 if (ch[fail_temp].next[i] != 0) //如果fail_temp节点的子节点有('a'+i)这个字符 83 fail_temp = ch[fail_temp].next[i]; 84 ch[ch[temp].next[i]].fail = fail_temp; //子节点的失配节点由父节点节点决定 85 } 86 } 87 } 88 } 89 90 //寻找text[]总共有多少个单词 91 void find(char *text, int x) 92 { 93 int len = strlen(text); 94 int node = 0, fail_temp = 0; 95 for (int i = 0; i < len; i++) 96 { 97 int character = idx(text[i]); 98 while (fail_temp > 0 && ch[fail_temp].next[character] == 0) 99 //若没有text[i]这个字符,则使用失配指针继续在Trie树上遍历 100 fail_temp = ch[fail_temp].fail; 101 102 if (ch[fail_temp].next[character] != 0) 103 { //此时找到了text[i]这个字符 104 fail_temp = ch[fail_temp].next[character]; 105 int fail_temp2 = fail_temp;//此处必须有另一个变量来记录失配的时候的下标 106 while (fail_temp2 > 0 && ch[fail_temp2].val) 107 { 108 for (int j = 0; j < ch[fail_temp2].line.size(); j++) 109 { 110 //枚举该单词所在的所有行 111 int temp = ch[fail_temp2].line[j]; 112 if (x >= temp) 113 cnt[x - temp][i] ++; 114 } 115 fail_temp2 = ch[fail_temp2].fail; 116 } 117 } 118 } 119 } 120 }AC; 121 122 int main() 123 { 124 int T; 125 scanf("%d", &T); 126 while (T--) 127 { 128 AC.init(); 129 memset(cnt, 0, sizeof(cnt)); 130 scanf("%d%d", &N, &M); 131 for (int i = 0; i < N; i++) 132 scanf("%s", map[i]); 133 scanf("%d%d", &X, &Y); 134 char str_temp[105]; 135 for (int i = 0; i < X; i++) 136 { 137 scanf("%s", str_temp); 138 AC.insert(str_temp, i); 139 } 140 AC.get_fail(); 141 for (int i = 0; i < N; i++) 142 { 143 AC.find(map[i], i); 144 } 145 int ans = 0; 146 for (int i = 0; i < N; i++) 147 { 148 for (int j = 0; j < M; j++) 149 { 150 if (cnt[i][j] == X) 151 ans++; 152 } 153 } 154 printf("%d ", ans); 155 } 156 return 0; 157 }