【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 }
View Code

2946: [Poi2000]公共串

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 160  Solved: 67
[Submit][Status][Discuss]

Description

 
       给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l        读入单词
l        计算最长公共子串的长度
l        输出结果
 

Input

 
文件的第一行是整数 n1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000
 

Output

仅一行,一个整数,最长公共子串的长度。
 

Sample Input

3
abcb
bca
acbc

Sample Output

HINT

Source

[Submit][Status][Discuss]
posted @ 2015-04-07 20:43  Tunix  阅读(292)  评论(0编辑  收藏  举报