题面
解析
翻译一下题意:给定n个字符串,求最长的字符串 S 的长度,使得 S 至少为其中 n/2+1 个字符串的子串, 并输出所有的S,若不存在,则输出'?'。(n<=100)
此题与POJ3261类似(我的博客),都是相同套路,后缀数组+二分答案
当然是把每个字符串连在一起,组成一个大的字符串,再在上面建立后缀数组。但是这里有个细节,搞了我很久,为了防止我们选出的子串跨越了原来的多个字符串,需要在每个字符串中间加入不同的不会出现的字符,这样就解决了这个问题。
check的时候需要维护每个块内的$lcp$出现在多少个原来的字符串中,开一个$bool$型的$vis$数组存原字符串是否在当前块内出现,再开一个$pos$数组存长字符串的下标所属的原字符串编号,对于字符串间加入的字符的$pos$等于0, 而$vis[0]$应永远等于$true$,不能对答案产生贡献
代码:

#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn = 105, maxm = 1005; int n, len, cur, timer, pos[maxn*maxm], s[maxn*maxm]; char c[maxm]; int sa[maxn*maxm], rk[maxn*maxm], tong[maxn*maxm], fir[maxn*maxm], sec[maxn*maxm], hei[maxn*maxm]; void Build_SA() { int m = 150; for(int i = 1; i <= len; ++i) fir[i] = s[i]; for(int i = 0; i <= m; ++i) tong[i] = 0; for(int i = 1; i <= len; ++i) tong[fir[i]] ++; for(int i = 1; i <= m; ++i) tong[i] += tong[i-1]; for(int i = len; i; --i) sa[tong[fir[i]]--] = i; for(int k = 1; k <= len; k <<= 1) { int t = 0; for(int i = len - k + 1; i <= len; ++i) sec[++t] = i; for(int i = 1; i <= len; ++i) if(sa[i] - k > 0) sec[++t] = sa[i] - k; for(int i = 0; i <= m; ++i) tong[i] = 0; for(int i = 1; i <= len; ++i) tong[fir[sec[i]]] ++; for(int i = 1; i <= m; ++i) tong[i] += tong[i-1]; for(int i = len; i; --i) sa[tong[fir[sec[i]]]--] = sec[i], sec[i] = 0; swap(fir, sec); t = 0; fir[sa[1]] = ++t; for(int i = 2; i <= len; ++i) if(sec[sa[i]] != sec[sa[i-1]] || sec[sa[i]+k] != sec[sa[i-1]+k]) fir[sa[i]] = ++t; else fir[sa[i]] = t; if(t >= len) break; m = t; } } void Get_hei() { int h = 0; for(int i = 1; i <= len; ++i) rk[sa[i]] = i; for(int i = 1; i <= len ;++i) { int t = sa[rk[i]-1]; while(s[t+h] == s[i+h]) h++; hei[rk[i]] = h; h = max(0, h-1); } } bool vis[maxn]; bool check(int x) { for(int i = 1; i <= n; ++i) vis[i] = 0; int p = 1, num = 1; vis[0] = 1; vis[pos[sa[1]]] = 1; for(int i = 2; i <= len; ++i) { if(hei[i] < x) { if(num > n / 2) return 1; for(int j = p; j < i; ++j) vis[pos[sa[j]]] = 0; vis[0] = 1; p = i; num = 0; } if(!vis[pos[sa[i]]]) num ++, vis[pos[sa[i]]] = 1; } return num > n / 2; } void write(int x) { for(int i = 1; i <= n; ++i) vis[i] = 0; int p = 1, num = 1; vis[0] = 1; vis[pos[sa[1]]] = 1; for(int i = 2; i <= len; ++i) { if(hei[i] < x) { if(num > n / 2) { int cnt = 0, j = sa[i-1]; while(cnt < x) { if(pos[j] != 0) printf("%c", s[j]-maxn+'a'), cnt++; j++; } printf(" "); } for(int j = p; j < i; ++j) vis[pos[sa[j]]] = 0; vis[0] = 1; p = i; num = 0; } if(!vis[pos[sa[i]]]) num ++, vis[pos[sa[i]]] = 1; } if(num > n / 2) { int cnt = 0, j = sa[len]; while(cnt < x) { if(pos[j] != 0) printf("%c", s[j]-maxn+'a'), cnt++; j++; } printf(" "); } } void work() { int l = 0, r = len, mid, ans = 0; while(l <= r) { mid = (l + r)>>1; if(check(mid)) ans = mid, l = mid + 1; else r = mid - 1; } if(ans) write(ans); else printf("? "); } int main() { while(scanf("%d", &n), n != 0) { if(cur++) printf(" "); len = timer = 0; for(int i = 1; i <= n; ++i) { scanf("%s", c); int l = strlen(c); for(int j = 0; j < l; ++j) s[++len] = c[j] - 'a' + maxn, pos[len] = i; s[++len] = ++timer; pos[len] = 0; } if(n == 1) { for(int i = 1; i < len; ++i) printf("%c", s[i]-maxn+'a'); printf(" "); } else { Build_SA(); Get_hei(); work(); } } return 0; }