题目大意:有n个字符串,找出连续的一串长度最大的字符,并且在超过半数的字符串中出现,如果无解输出“?”,否则输出这个字符串,如果多解,按字典序输出,每两个样例之间一个空行。
思路:在每个字符串后面都加上一个没出现过的字符,然后把这n个都连起来,形成一个字符串,对这个字符串求 sa、height 。之后再二份答案长度,设当二分出的长度为m,那么就一遍 height 扫过去,出现比他小的,就新增加一个段,并统计前面一个段里出现了几个原字符串,>n/2 就 return 1。过程就是这样,但是这里还有个坑爹的地方,那就还要特判 n = 1 的情况,切记!另外,注意空行,否则 UVA 会报 WA 。其实这里面还有一个地方需要想明白:为什么 height 一遍扫下去就可以,如果有段大于 n /2,是表示成立,那么反过来呢,成不成立?想一下就知道,肯定也成立,因为它是按照字典序排的,有相同前缀的几个一定是在一起的。
自己做的时候,还发现了一个坑爹的地方,那就是我最后合并的数组 s 开的是char ,一直RE,一直以为是数组边界的问题,搞了半天。后来发现,原来是 100 + 30 会爆char ,因为char 才 0~ 127 ,坑啊。。= =
看了一天的后缀数组, A 的第一道题,虽然说跌跌撞撞,还被坑了好久,但是AC后顿时感觉还是不错的~~
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 111111; int num; int s[MAXN]; int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN]; void build_sa(int n,int m) { int *x = t,*y = t2; for(int i = 0 ;i<m;i++) c[i] = 0; for(int i = 0 ;i<n;i++) c[x[i] = s[i]]++; for(int i = 1 ;i<m;i++) c[i] += c[i-1]; for(int i = n-1;i >=0 ;i--) sa[--c[x[i]]] = i; for(int k = 1;k<=n;k <<= 1) { int p = 0; for(int i = n - k ;i < n;i ++) y[p++] = i; for(int i = 0;i < n;i++) if(sa[i] >= k) y[p++] = sa[i] - k; for(int i = 0;i<m;i++) c[i] = 0; for(int i = 0;i < n;i++) c[x[y[i]]]++; for(int i = 1;i<m;i++) c[i] += c[i-1]; for(int i = n - 1;i >= 0;i--) sa[--c[x[y[i]]]] = y[i]; swap(x,y); p = 1; x[sa[0]] = 0; for(int i = 1;i<n;i++) x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1 : p++; if(p >= n) break; m = p; } } int height[MAXN],rank[MAXN]; void get_height(int n) { for(int i = 1;i<=n;i++) rank[sa[i]] = i; int k = 0; for(int i = 0;i<n;i++) { if(k) k--; int j = sa[rank[i] - 1]; while(s[i + k] == s[j + k]) k++; height[rank[i]] = k; } } int belong[MAXN]; int flag[111]; int check(int len,int n) { memset(flag,0,sizeof(flag)); int cc = 0; for(int i = 1; i <= n; i++) { if(height[i] < len) { memset(flag,0,sizeof(flag)); cc = 0; } else { if(flag[belong[sa[i-1]]] == 0) { flag[belong[sa[i-1]]] = 1; cc++; } if(flag[belong[sa[i]]] == 0) { flag[belong[sa[i]]] = 1; cc++; } if(cc > num/2) return 1; } } return 0; } void print(int len,int n) { memset(flag,0,sizeof(flag)); int cc = 0; int ok = 0; for(int i = 0; i <= n; i++) { if(height[i] < len) { memset(flag,0,sizeof(flag)); cc = 0; ok = 0; } else { if(flag[belong[sa[i-1]]] == 0) { flag[belong[sa[i-1]]] = 1; cc++; } if(flag[belong[sa[i]]] == 0) { flag[belong[sa[i]]] = 1; cc++; } if(!ok && cc > num/2) { for(int j = 0;j<len;j++) printf("%c",s[sa[i] + j] + 'a' - 1); puts(""); ok = 1; } } } } void solve(int n) { int ans = 0; int l = 1,r = n; while(l <= r) { int mid = (l+r) >> 1; if(check(mid,n)) { ans = mid; l = mid + 1; } else r = mid - 1; //printf("l = %d,r = %d,mid = %d\n",l,r,mid); } //printf("ans = %d\n",ans); if(ans) print(ans,n); else printf("?\n"); } char str[1111]; int main() { int first = 1; while(~scanf("%d",&num) && num) { if(first) first = 0; else puts(""); int end = 30; int n = 0; for(int i = 0;i<num;i++) { scanf("%s",str); int len = strlen(str); for(int j = 0;j<len;j++) { s[n] = str[j] - 'a' + 1; belong[n] = i; n++; } s[n] = end++; belong[n] = num; n++; } s[n] = 0; if(num == 1) { puts(str); continue; } build_sa(n+1,end); get_height(n); solve(n); } return 0; }