题目链接:http://poj.org/problem?id=3294
本题练习后缀数组和LCP.二分查找长度是否满足条件即可。
后缀数组模板:
int sa[Maxn],t[Maxn],t2[Maxn],c[Maxn]; int rank[Maxn],height[Maxn]; //传递的两个参数:n是数组大小,m是数组内元素的Ascii码的最大值+1 void build_sa(int n,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; //直接利用sa数组排序第二关键字 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 = 0; i < m; i++) c[i] += c[i-1]; for(i = n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i]; //根据sa 和y数组计算新的x数组 swap(x,y); p = 1; x[sa[0]] = 0; for(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; } } //注意getHeight传递入的参数是原数组的长度-1.切记 void getHeight(int n) { int i, j, k = 0; for(i = 1; i <= n; ++i) rank[sa[i]] = i; for(i = 0; i < n; height[rank[i++]] = k) for(k? k-- : 0, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; ++k); return; }本题要注意两点:getHeight(n)中传递的参数是数组长度-1,然后二分法的时候边界要小心,n=1时,直接输出原字符串即可。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <map> #include <queue> #include <algorithm> using namespace std; #define Maxn 250000 #define Maxm 155 int s[Maxn]; int sa[Maxn],t[Maxn],t2[Maxn],c[Maxn]; int rank[Maxn],height[Maxn]; int tot;//整合后串的长度 //原串的个数 int n; //整合后的新串中序号为i的字符归属的原串序号 int num[Maxn]; //当前段中含有的原串的后缀的个数 int flag[Maxm]; //最终解的后缀的序号(多组解不唯一) int res[Maxn]; //最终解的个数 int ans_num; void build_sa(int n,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; //直接利用sa数组排序第二关键字 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 = 0; i < m; i++) c[i] += c[i-1]; for(i = n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i]; //根据sa 和y数组计算新的x数组 swap(x,y); p = 1; x[sa[0]] = 0; for(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; } } void getHeight(int n) { int i, j, k = 0; for(i = 1; i <= n; ++i) rank[sa[i]] = i; for(i = 0; i < n; height[rank[i++]] = k) for(k? k-- : 0, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; ++k); return; } bool possible(int L) { memset(flag,0,sizeof(flag)); int t = 0;//包含原串的个数 int lock = 0; for(int i=1; i<tot; i++) { if(height[i] >= L) { flag[num[sa[i]]] = flag[num[sa[i-1]]] = 1; } else { for(int j=0; j<n; j++) if(flag[j]) t++; if(t>n/2) { if(lock == 0) { ans_num = 0; lock = 1; } res[ans_num++] = sa[i-1]; } memset(flag,0,sizeof(flag)); t = 0; } } //处理最后的段 t = 0; for(int j=0; j<n; j++) if(flag[j]) t++; if(t>n/2) { if(lock == 0) { ans_num = 0; lock = 1; } res[ans_num++] = sa[n-1]; } if(lock == 1) return true; return false; } void binarySearch(int _l,int _r) { int l = _l,r = _r; int mid;//最长的长度 int ans_len = l; int lock = 0; while(l < r) { mid = (l + r + 1)/2; if(possible(mid)) { lock = 1; ans_len = mid; l = mid; } else { r = mid - 1; } } if(possible(ans_len)) lock = 1; if(!lock) { puts("?"); return; } for(int i=0; i<ans_num; i++) { for(int j=res[i]; j<res[i] + ans_len; j++) printf("%c",s[j] - 1 + 'a'); puts(""); } } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int len; int r = 0,l = 0; char temp[1500]; int cas = 0; while(scanf(" %d",&n)!=EOF && n!=0) { cas++; if(cas!=1) puts(""); tot = 0; l = 1; r = 0;//最长的长度 for(int i=0; i<n; i++) { scanf(" %s",temp); len = strlen(temp); if(len > r) r = len; for(int j=0; j<len; j++) { num[tot] = i; s[tot++] = temp[j] - 'a' + 1; } s[tot++] = 26 + i + 1; } s[tot++] = 0; if(n == 1) { printf("%s\n",temp); continue; } build_sa(tot,26 + n + 1); //注意传递的参数是长度-1 getHeight(tot-1); binarySearch(l,r); } return 0; }