UVa 11107 (后缀数组 二分) Life Forms
2015-04-20 16:58
239 查看
利用height值对后缀进行分组的方法很常用,好吧,那就先记下了。
题意:
给出n个字符串,求一个长度最大的字符串使得它在超过一半的字符串中出现。
多解的话,按字典序输出全部解。
分析:
在所有输入的字符串后面加一个原串中没有的且互不相同的字符,然后将新得到的n个字符串拼接成一个长的字符串。(为什么要加互不相同的分割字符,这里始终想不明白)
首先二分最大公共字串的长度p。扫描一遍height数组,每遇到一个height[i] < p便开辟一个新段,这样就将height数组拆分为若干段。而且每一段的所有字符都有一个长度为p的公共前缀。只要某一段中包含了超过 n / 2 的原串的后缀,就满足条件了。
如何判断是否包含了某个原串的后缀,用一个flag标记数组即可实现。
代码君
题意:
给出n个字符串,求一个长度最大的字符串使得它在超过一半的字符串中出现。
多解的话,按字典序输出全部解。
分析:
在所有输入的字符串后面加一个原串中没有的且互不相同的字符,然后将新得到的n个字符串拼接成一个长的字符串。(为什么要加互不相同的分割字符,这里始终想不明白)
首先二分最大公共字串的长度p。扫描一遍height数组,每遇到一个height[i] < p便开辟一个新段,这样就将height数组拆分为若干段。而且每一段的所有字符都有一个长度为p的公共前缀。只要某一段中包含了超过 n / 2 的原串的后缀,就满足条件了。
如何判断是否包含了某个原串的后缀,用一个flag标记数组即可实现。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1001 * 100 + 10; struct SuffixArray { int s[maxn]; int sa[maxn]; int rank[maxn]; int height[maxn]; int t[maxn], t2[maxn], c[maxn]; int n; void clear() { n = 0; memset(sa, 0, sizeof(sa)); } void build_sa(int m) { int i, *x = t, *y = t2; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[i] = s[i]]++; for(i = 1; i < m; i++) c[i] += c[i - 1]; for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; for(int k = 1; k <= n; k <<= 1) { int p = 0; for(i = n - k; i < n; i++) y[p++] = i; for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[y[i]]]++; for(i = 1; i < m; i++) c[i] += c[i - 1]; for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i++) x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p - 1 : p++; if(p >= n) break; m = p; } } void build_height() { int i, j, k = 0; for(i = 0; i < n; i++) rank[sa[i]] = i; for(i = 0; i < n; i++) { if(k) k--; j = sa[rank[i] - 1]; while(s[i + k] == s[j + k]) k++; height[rank[i]] = k; } } }; const int maxc = 100 + 10; const int maxl = 1000 + 10; SuffixArray sa; int n; char word[maxl]; int idx[maxn]; bool flag[maxc]; void print_sub(int L, int R) { for(int i = L; i < R; i++) printf("%c", sa.s[i] - 1 + 'a'); puts(""); } bool good(int L, int R) { memset(flag, false, sizeof(flag)); int cnt = 0; for(int i = L; i < R; i++) { int x = idx[sa.sa[i]]; if(x != n && !flag[x]) { flag[x] = true; cnt++; } } return cnt > n / 2; } bool print_solution(int len, bool print) { int L = 0; for(int R = 1; R <= sa.n; R++) { if(R == sa.n || sa.height[R] < len) { if(good(L, R)) { if(print) print_sub(sa.sa[L], sa.sa[L] + len); else return true; } L = R; } } return false; } void solve(int maxlen) { if(!print_solution(1, false)) puts("?"); else { int L = 1, R = maxlen, M; while(L < R) { M = L + (R - L + 1) / 2; if(print_solution(M, false)) L = M; else R = M - 1; } print_solution(L, true); } } void add(int ch, int i) { idx[sa.n] = i; sa.s[sa.n++] = ch; } int main() { //freopen("in.txt", "r", stdin); int kase = 0; while(scanf("%d", &n) == 1 && n) { if(kase++ > 0) puts(""); sa.clear(); int maxlen = 0; for(int i = 0; i < n; i++) { scanf("%s", word); int sz = strlen(word); maxlen = max(maxlen, sz); for(int j = 0; j < sz; j++) add(word[j] - 'a' + 1, i); add(i + 100, n); } add(0, n); sa.build_sa(100 + n); sa.build_height(); solve(maxlen); } return 0; }
代码君
相关文章推荐
- UVA 11107 Life Forms(后缀数组 OR hash)
- POJ 3294 Life Forms <后缀数组+二分>
- UVA 11107 Life Forms(后缀数组+二分)
- Uva 11107 Life Forms(后缀数组 + 二分)
- UVA 11107 Life Forms (后缀数组 + 二份答案)
- POJ 3294 (UVA 11107) Life Forms 后缀数组
- UVa11107 后缀数组
- UVA 11107 Life Forms 后缀数组
- 后缀数组 UVA 11107 Life Forms
- 连续子串中出现超过一半次数的字符串 后缀数组 uva 11107 Life Forms
- UVA 12206 Stammering Aliens(后缀数组+二分)
- 后缀数组,LCP(生命的形式,UVA 11107)
- uva11107 后缀数组
- UVa 11107 - Life Forms (后缀数组 求出现K次的串 根据height分组)
- 【后缀数组】[UVA10829]L-Gap substring
- POJ 3261 Milk Patterns <后缀数组+二分>
- UVA 题目11512 - GATTACA(后缀数组求出现次数最多的子串及重复次数)
- UVA 题目1223 - Editor(后缀数组求出现次数超过两次的最长子串的长度)
- poj3080 Blue Jeans(后缀数组+二分答案)
- BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )