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 }