poj 3518 Corporate Identity 后缀数组->多字符串最长相同连续子串
题意:输入N(2 <= N <= 4000)个长度不超过200的字符串,输出字典序最小的最长公共连续子串;
思路:将所有的字符串中间加上分隔符,注:分隔符只需要和输入的字符不同,且各自不同即可,没有必要是最小的字符;
连接后缀数组求解出height之后二分长度,由于height是根据sa数组建立的,所以前面符合的就是字典序最小的,直接找到就停止即可;
ps: 把之前的模板简化了下,A题才是关键;
#include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> using namespace std; #define MS0(a) memset(a,0,sizeof(a)) typedef long long ll; const int MAXN = 1000005; int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],wv[MAXN]; int cmp(int *r, int a, int b, int l){ return r[a] == r[b] && r[a+l] == r[b+l]; } void build_sa(int *r, int n, int m){ // 倍增算法 r为待匹配数组 n为总长度 m为字符范围 int i, j, p, *x = t, *y = t2; for(i = 0; i < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[x[i] = r[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(j = 1, p = 1; p < n; j <<= 1, m = p){ for(p = 0, i = n-j; i < n; i ++) y[p++] = i; for(i = 0; i < n; i ++) if(sa[i] >= j) y[p++] = sa[i] - j; for(i = 0; i < n; i ++) wv[i] = x[y[i]]; for(i = 0; i < m; i ++) c[i] = 0; for(i = 0; i < n; i ++) c[wv[i]] ++; for(i = 1; i < m; i ++) c[i] += c[i-1]; for(i = n-1; i >= 0; i --) sa[--c[wv[i]]] = y[i]; for(swap(x,y), p = 1, x[sa[0]] = 0, i = 1; i < n; i++){ x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p++; } } } int rk[MAXN],height[MAXN]; void getHeight(int *r,int n) { for(int i = 1;i <= n;i++) rk[sa[i]] = i; // rk[i]:后缀i在sa[]中的下标 for(int i = 0,j,k = 0; i < n; height[rk[i++]] = k){ for(k? k--:0 ,j = sa[rk[i] - 1];r[i+k] == r[j+k];k++); } } int d[MAXN],num[MAXN],vs[4444],T,len,tot; char tt[222]; bool check(int L) { MS0(vs); int cnt = 0; for(int i = 2;i <= tot;i++){ if(height[i] < L){ MS0(vs); cnt = 0; continue; } if(!vs[d[sa[i]]]){ vs[d[sa[i]]] = 1;cnt++; } if(!vs[d[sa[i-1]]]){ vs[d[sa[i-1]]] = 1;cnt++; } if(cnt == T){ //对于同一个长度,我们只取第一次出现的符合条件的字符串; for(int j = 0; j<L; j++) tt[j] = num[sa[i]+j]; tt[L] = '\0'; return true; } } return false; } char str[222]; int main() { while(scanf("%d",&T) == 1 && T){ tot = 0; for(int i = 1;i <= T;i++){ scanf("%s",str); len = strlen(str); for(int j = 0;j < len;j++) num[tot] = str[j],d[tot++] = i; num[tot] = 'z'+i,d[tot++] = 'z'+i;// '#' + i竟然WA了 } num[tot] = 0;//最后添加一个最小字符; build_sa(num,tot+1,5155); getHeight(num,tot); int ans = 0,l = 1,r = len,mid,id; while(l <= r){ mid = l + r >> 1; if(check(mid)){ans = mid,l = mid + 1;} else r = mid - 1; } if(ans){ printf("%s\n",tt); } else puts("IDENTITY LOST"); } return 0; }