UVa 11107 (后缀数组 二分) Life Forms
利用height值对后缀进行分组的方法很常用,好吧,那就先记下了。
题意:
给出n个字符串,求一个长度最大的字符串使得它在超过一半的字符串中出现。
多解的话,按字典序输出全部解。
分析:
在所有输入的字符串后面加一个原串中没有的且互不相同的字符,然后将新得到的n个字符串拼接成一个长的字符串。(为什么要加互不相同的分割字符,这里始终想不明白)
首先二分最大公共字串的长度p。扫描一遍height数组,每遇到一个height[i] < p便开辟一个新段,这样就将height数组拆分为若干段。而且每一段的所有字符都有一个长度为p的公共前缀。只要某一段中包含了超过 n / 2 的原串的后缀,就满足条件了。
如何判断是否包含了某个原串的后缀,用一个flag标记数组即可实现。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 1001 * 100 + 10; 7 8 struct SuffixArray 9 { 10 int s[maxn]; 11 int sa[maxn]; 12 int rank[maxn]; 13 int height[maxn]; 14 int t[maxn], t2[maxn], c[maxn]; 15 int n; 16 17 void clear() { n = 0; memset(sa, 0, sizeof(sa)); } 18 19 void build_sa(int m) 20 { 21 int i, *x = t, *y = t2; 22 for(i = 0; i < m; i++) c[i] = 0; 23 for(i = 0; i < n; i++) c[x[i] = s[i]]++; 24 for(i = 1; i < m; i++) c[i] += c[i - 1]; 25 for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; 26 for(int k = 1; k <= n; k <<= 1) 27 { 28 int p = 0; 29 for(i = n - k; i < n; i++) y[p++] = i; 30 for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k; 31 for(i = 0; i < m; i++) c[i] = 0; 32 for(i = 0; i < n; i++) c[x[y[i]]]++; 33 for(i = 1; i < m; i++) c[i] += c[i - 1]; 34 for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; 35 swap(x, y); 36 p = 1; x[sa[0]] = 0; 37 for(i = 1; i < n; i++) 38 x[sa[i]] = y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k] ? p - 1 : p++; 39 if(p >= n) break; 40 m = p; 41 } 42 } 43 44 void build_height() 45 { 46 int i, j, k = 0; 47 for(i = 0; i < n; i++) rank[sa[i]] = i; 48 for(i = 0; i < n; i++) 49 { 50 if(k) k--; 51 j = sa[rank[i] - 1]; 52 while(s[i + k] == s[j + k]) k++; 53 height[rank[i]] = k; 54 } 55 } 56 }; 57 58 const int maxc = 100 + 10; 59 const int maxl = 1000 + 10; 60 SuffixArray sa; 61 int n; 62 char word[maxl]; 63 int idx[maxn]; 64 bool flag[maxc]; 65 66 void print_sub(int L, int R) 67 { 68 for(int i = L; i < R; i++) printf("%c", sa.s[i] - 1 + 'a'); 69 puts(""); 70 } 71 72 bool good(int L, int R) 73 { 74 memset(flag, false, sizeof(flag)); 75 int cnt = 0; 76 for(int i = L; i < R; i++) 77 { 78 int x = idx[sa.sa[i]]; 79 if(x != n && !flag[x]) { flag[x] = true; cnt++; } 80 } 81 return cnt > n / 2; 82 } 83 84 bool print_solution(int len, bool print) 85 { 86 int L = 0; 87 for(int R = 1; R <= sa.n; R++) 88 { 89 if(R == sa.n || sa.height[R] < len) 90 { 91 if(good(L, R)) 92 { 93 if(print) print_sub(sa.sa[L], sa.sa[L] + len); 94 else return true; 95 } 96 L = R; 97 } 98 } 99 return false; 100 } 101 102 void solve(int maxlen) 103 { 104 if(!print_solution(1, false)) puts("?"); 105 else 106 { 107 int L = 1, R = maxlen, M; 108 while(L < R) 109 { 110 M = L + (R - L + 1) / 2; 111 if(print_solution(M, false)) L = M; 112 else R = M - 1; 113 } 114 print_solution(L, true); 115 } 116 } 117 118 void add(int ch, int i) 119 { 120 idx[sa.n] = i; 121 sa.s[sa.n++] = ch; 122 } 123 124 int main() 125 { 126 //freopen("in.txt", "r", stdin); 127 128 int kase = 0; 129 while(scanf("%d", &n) == 1 && n) 130 { 131 if(kase++ > 0) puts(""); 132 sa.clear(); 133 int maxlen = 0; 134 for(int i = 0; i < n; i++) 135 { 136 scanf("%s", word); 137 int sz = strlen(word); 138 maxlen = max(maxlen, sz); 139 for(int j = 0; j < sz; j++) add(word[j] - 'a' + 1, i); 140 add(i + 100, n); 141 } 142 add(0, n); 143 144 sa.build_sa(100 + n); 145 sa.build_height(); 146 solve(maxlen); 147 } 148 149 return 0; 150 }
代码君
转载于:https://www.cnblogs.com/AOQNRMGYXLMV/p/4442030.html
UVa 11107 (后缀数组 二分) Life Forms相关推荐
- HDU - 5030 Rabbit's String(后缀数组+二分)
题目链接:点击查看 题目大意:给出一个字符串,现在要求将其分为不大于k个连续的子串,对于每个子串求出字典序最大的子串,现在要求所有子串的最大子串的最大值最小,输出这个最大子串 题目分析:最大值最小,标 ...
- HDU - 5008 Boring String Problem(后缀数组+二分)
题目链接:点击查看 题目大意:给出一个字符串,接下来给出 q 个询问,每次询问字符串中第 k 大的子串,要求输出该字串的左右端点,如果有多个答案,输出左端点最小的一个 题目分析:因为在求出后缀数组后, ...
- bzoj3277 串 (后缀数组+二分答案+ST表)
常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...
- SPOJ 220 Relevant Phrases of Annihilation(后缀数组+二分答案)
[题目链接] http://www.spoj.pl/problems/PHRASES/ [题目大意] 求在每个字符串中出现至少两次的最长的子串 [题解] 注意到这么几个关键点:最长,至少两次,每个字符 ...
- 洛谷 P4094 [HEOI2016/TJOI2016]字符串 后缀数组+二分+主席树
题目链接 后缀数组 题目分析: sa[i] – 第i小的后缀的编号 rank[i] --编号为i的后缀排第几: height[i] – 第i和第i-1的最长lcp最长公共前缀: 1.二分答案,答案肯定 ...
- [BZOJ4310]跳蚤-后缀数组-二分答案
跳蚤 Description 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究.首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择 ...
- POJ - 1226 Substrings(后缀数组+二分)
题目链接:点击查看 题目大意:给出n个字符串,求出n个字符串中或者在他们翻转过来的字符串中,出现的最长公共子串的长度 题目分析:说白了就是求n个字符串中的最长公共子串的变形,而这个变形也相当简单,只是 ...
- BZOJ3998 TJOI2015弦论(后缀数组+二分答案)
先看t=1的情况.显然得求出SA(因为我不会SAM).我们一位位地确定答案.设填到了第len位,二分这一位填什么之后,在已经确定的答案所在的范围(SA上的某段区间)内二分,找到最后一个小于当前串的后缀 ...
- Musical Theme POJ - 1743(后缀数组+二分)
求不可重叠最长重复子串 对于height[i]定义为sa[i]和 sa[i-1]的最长公共前缀 这个最长公共前缀的值肯定是最大的 证明: 设rank[j] < rank[k], 则不难证明后缀j ...
最新文章
- Android开发--多媒体应用开发(一)--MediaPlayer的使用介绍
- STL源码剖析---STL容器特征总结(含迭代器失效)
- 【数据结构与算法】之深入解析“打家劫舍II”的求解思路与算法示例
- 基础知识回顾——异常处理
- 关于java中求和的方法,用可变变量来解决这个问题
- 深度学习 | MATLAB卷积神经网络原理描述
- GIS软件开发入门需要学习哪些内容?
- HART转PROFIBUS DP(V0)+RS485方法
- HUSTOJ配置文件解释
- HTML 教程-菜鸟教程
- King Arthur's Knights 【HDU - 4337】【哈密顿回路性质Dirac定理】
- 谈谈如何做到从未来看向当代的能源技术
- ICE java实现helloworld
- 【C++]参数的缺省值
- 人生如逆旅,我亦是行人
- css 好看滚动条样式大全,CSS 个性化滚动条样式
- 65W氮化镓Switch底座扩展坞方案
- Matlab中num2str函数的用法
- 密度峰值聚类算法介绍(DPC)
- Naked dog come out