【题目链接】
http://poj.org/problem?id=3294
【题意】
多个字符串求出现超过R次的最长公共子串。
【思路】
二分+划分height,判定一个组中是否包含不小于R个不同字符串的后缀。
需要注意的有:
1) c[]尽量开大,字符范围为“偏移”之后的范围。
2) 用kase作为标记节省了每次开始新段需要清零的时间。
3) 因为height是sa[i]与sa[i-1]的关系,所以无论是在can的开始还是在新段开始都需要初始为一个串的情况。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 6 using namespace std; 7 8 const int maxn = 200000+10; 9 10 int s[maxn]; 11 int sa[maxn],c[maxn],t[maxn],t2[maxn]; 12 13 void build_sa(int m,int n) { 14 int i,*x=t,*y=t2; 15 for(i=0;i<m;i++) c[i]=0; 16 for(i=0;i<n;i++) c[x[i]=s[i]]++; 17 for(i=1;i<m;i++) c[i]+=c[i-1]; 18 for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; 19 20 for(int k=1;k<=n;k<<=1) { 21 int p=0; 22 for(i=n-k;i<n;i++) y[p++]=i; 23 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 24 25 for(i=0;i<m;i++) c[i]=0; 26 for(i=0;i<n;i++) c[x[y[i]]]++; 27 for(i=0;i<m;i++) c[i]+=c[i-1]; 28 for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; 29 30 swap(x,y); 31 p=1; x[sa[0]]=0; 32 for(i=1;i<n;i++) 33 x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++; 34 if(p>=n) break; 35 m=p; 36 } 37 } 38 int rank[maxn],height[maxn]; 39 void getHeight(int n) { 40 int i,j,k=0; 41 for(i=0;i<=n;i++) rank[sa[i]]=i; 42 for(i=0;i<n;i++) { 43 if(k) k--; 44 j=sa[rank[i]-1]; 45 while(s[j+k]==s[i+k]) k++; 46 height[rank[i]]=k; 47 } 48 } 49 50 int T; 51 char a[maxn]; 52 53 int f[200],kase; 54 vector<int> st; 55 int can(int limit,int n,int len) { 56 int cnt=1,ok=0; 57 st.clear(); 58 f[sa[1]/len]=kase; 59 for(int i=2;i<=n;i++) { 60 if(height[i]<limit) { 61 cnt=1; 62 f[sa[i]/len]=++kase; //检查每一个组中 63 } 64 else { 65 if(f[sa[i]/len]!=kase) { 66 f[sa[i]/len]=kase; 67 if(cnt>=0) cnt++; 68 if(cnt>T/2) { 69 ok=1; 70 st.push_back(sa[i]); 71 cnt=-1; 72 } 73 } 74 } 75 } 76 return ok; 77 } 78 void init() { 79 kase=1; 80 memset(sa,0,sizeof(sa)); 81 memset(f,0,sizeof(f)); 82 } 83 int main() { 84 //freopen("in.in","r",stdin); 85 //freopen("out.out","w",stdout); 86 while(scanf("%d",&T)==1 && T) { 87 init(); 88 int len,n=0; 89 for(int i=0;i<T;i++) { 90 scanf("%s",&a); 91 len=strlen(a); 92 for(int j=0;j<len;j++) s[n++]=a[j]+100; 93 s[n++]=i+1; 94 } 95 n--; 96 s[n]=0; 97 98 build_sa(250,n+1); 99 getHeight(n); 100 101 int L=0,R=len+1; 102 while(L<R) { 103 int M=L+(R-L+1)/2; 104 if(can(M,n,len+1)) L=M; 105 else R=M-1; 106 } 107 can(L,n,len+1); //再调用一次求出st 108 if(L==0) printf("? "); 109 else { 110 for(int i=0;i<st.size();i++) { 111 for(int j=st[i];(j-st[i]+1)<=L;j++) 112 printf("%c",s[j]-100); 113 putchar(' '); 114 } 115 } 116 putchar(' '); 117 } 118 return 0; 119 }