【BZOJ】【2946】【POI2000】公共串
后缀数组
好感动,复习了下后缀数组居然写出来了……(感谢ykz大神)
求最长公共子串……WA了一发是因为:【不同字符串之间要用不同的特殊字符隔开】否则就会匹配到相同→_→比如都是aaa结尾,如果用相同特殊字符就会使得最长公共子串变成aaa#这样子……
1 /************************************************************** 2 Problem: 2946 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:60 ms 7 Memory:4104 kb 8 ****************************************************************/ 9 10 //BZOJ 2946 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 typedef long long LL; 23 inline int getint(){ 24 int r=1,v=0; char ch=getchar(); 25 for(;!isdigit(ch);ch=getchar()) if(ch=='-')r=-1; 26 for(; isdigit(ch);ch=getchar()) v=v*10+ch-'0'; 27 return r*v; 28 } 29 const int N=1e5+10,INF=~0u>>2; 30 /*******************template********************/ 31 int sa[N],rank[N],height[N],belong[N],wa[N],wb[N],c[N],n,m; 32 int len[6]; 33 char s[N]; 34 bool cmp(int *r,int a,int b,int l){ 35 return r[a]==r[b] && r[a+l]==r[b+l]; 36 } 37 void DA(char *s,int *sa,int n,int m){ 38 int i,j,p,*x=wa,*y=wb; 39 rep(i,m) c[i]=0; 40 rep(i,n) c[x[i]=s[i]]++; 41 F(i,1,m-1) c[i]+=c[i-1]; 42 D(i,n-1,0) sa[--c[x[i]]]=i; 43 for(j=1,p=0;p<n;j<<=1,m=p){ 44 for(p=0,i=n-j;i<n;i++) y[p++]=i; 45 rep(i,n) if (sa[i]>=j) y[p++]=sa[i]-j; 46 47 rep(i,m) c[i]=0; 48 rep(i,n) c[x[y[i]]]++; 49 F(i,1,m-1) c[i]+=c[i-1]; 50 D(i,n-1,0) sa[--c[x[y[i]]]]=y[i]; 51 swap(x,y); p=1; x[sa[0]]=0; 52 F(i,1,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p-1 : p++; 53 } 54 } 55 void calheight(char *s,int *sa,int n){ 56 int k=0; 57 F(i,1,n) rank[sa[i]]=i; 58 rep(i,n){ 59 if (k) k--; 60 int j=sa[rank[i]-1]; 61 while(s[i+k]==s[j+k]) k++; 62 height[rank[i]]=k; 63 } 64 } 65 int main(){ 66 #ifndef ONLINE_JUDGE 67 freopen("2946.in","r",stdin); 68 freopen("2946.out","w",stdout); 69 #endif 70 n=getint();int l=0; 71 F(i,1,n){ 72 scanf("%s",s+l); 73 len[i]=strlen(s)-l; 74 l=strlen(s); 75 s[l++]='a'-n+i; 76 } 77 rep(i,l) s[i]=s[i]-'a'+10; 78 l--; 79 DA(s,sa,l+1,40); 80 calheight(s,sa,l); 81 int tmp=1; 82 rep(i,l){ 83 if (s[i]<10) {tmp++;continue;} 84 belong[rank[i]]=tmp; 85 } 86 87 88 bool vis[6]={0}; 89 int L=0,R=2000,mid,cnt,ans=0; 90 while(L<=R){ 91 mid=(L+R)>>1;bool sign=0; 92 cnt=0; memset(vis,0,sizeof vis); 93 F(i,n,l){ 94 if (height[i]<mid){ 95 cnt=1;memset(vis,0,sizeof vis); 96 vis[belong[i]]=1; continue; 97 } 98 if (!vis[belong[i]]) vis[belong[i]]=1,cnt++; 99 if (cnt==n) sign=1; 100 } 101 if (sign) ans=mid,L=mid+1; 102 else R=mid-1; 103 } 104 printf("%d\n",ans); 105 return 0; 106 }
2946: [Poi2000]公共串
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 160 Solved: 67
[Submit][Status][Discuss]
Description
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
Input
文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
Output
仅一行,一个整数,最长公共子串的长度。
Sample Input
3
abcb
bca
acbc
abcb
bca
acbc
Sample Output
2