POJ 3294 Life Forms <后缀数组+二分>
2017-05-01 12:48
387 查看
题目:传送门。
分析:求重复出现大于n/2次的最长子串,有多个时按字典序输出。对长度进行二分。RE了几次,注意满足条件的子串有可能有超过1000个。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int MAXN=110;
const int MAXL=1010;
int wm[MAXN*MAXL],wa[MAXN*MAXL],wb[MAXN*MAXL],height[MAXN*MAXL],sa[MAXN*MAXL];
int *rank;
bool cmp(int *r,int a,int b,int len){
return r[a]==r[b]&&r[a+len]==r[b+len];
}
void da(int *data,int *sa,int n,int m){
int *x=wa,*y=wb,*t,i,j,p;
for(i=0;i<m;++i) wm[i]=0;
for(i=0;i<n;++i) ++wm[x[i]=data[i]];
for(i=1;i<m;++i) wm[i]+=wm[i-1];
for(i=n-1;i>=0;--i) sa[--wm[x[i]]]=i;
for(j=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<m;++i) wm[i]=0;
for(i=0;i<n;++i) ++wm[x[y[i]]];
for(i=1;i<m;++i) wm[i]+=wm[i-1];
for(i=n-1;i>=0;--i) sa[--wm[x[y[i]]]]=y[i];
for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
}
rank=x;
}
void calHeight(int *data,int *sa,int n){
for(int i=0,j,k=0;i<n;height[rank[i++]]=k)
for(k?--k:k,j=sa[rank[i]-1];data[i+k]==data[j+k];++k);
}
int len[MAXN],data[MAXL*MAXN];
int n,a[MAXL],t,cas=0;
char str[MAXL];
int nn;
bool vis[MAXN];
bool judge(int _len,int x){
t=0;
int flag,counte=0;
for(int i=1;i<=_len;++i){
if(height[i]>=x){
int posi1=sa[i-1],posi2=sa[i];
if(!(flag++)) memset(vis,false,sizeof vis);
for(int j=1;j<=n;++j){
if(posi1>=len[j-1]&&posi1<len[j]) counte+=vis[j-1]?0:1,vis[j-1]=true;
if(posi2>=len[j-1]&&posi2<len[j]) counte+=vis[j-1]?0:1,vis[j-1]=true;
if(counte>n/2) break;
}
}
else{
if(counte>n/2) a[t++]=i-1;
flag=counte=0;
}
}
if(counte>n/2) a[t++]=_len;
if(t){
nn=t;
return true;
}
else return false;
}
void solve(int _len){
int l=0,r=MAXL;
while(l<r){
int mid=l+(r-l+1)/2;
if(judge(_len,mid)) l=mid;
else r=mid-1;
}
if(!l) cout<<"?\n";
else{
for(int i=0;i<nn;++i){
int star=sa[a[i]];
for(int j=star;j<star+l;++j){
printf("%c",data[j]);
}
printf("\n");
}
}
}
int main(){
int p;
while(cin>>n,n){
p=len[0]=0;
for(int i=0;i<n;++i){
scanf("%s",str);
int temp_len=strlen(str);
len[i+1]=len[i]+temp_len+1;
for(int j=0;j<temp_len;++j){
data[p++]=str[j];
}
data[p++]=130+i;
}
if(cas++) cout<<endl;
if(n==1){
printf("%s\n",str);
continue;
}
data[p-1]=0;
da(data,sa,p,300);
calHeight(data,sa,p-1);
solve(p-1);
}
return 0;
}
分析:求重复出现大于n/2次的最长子串,有多个时按字典序输出。对长度进行二分。RE了几次,注意满足条件的子串有可能有超过1000个。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int MAXN=110;
const int MAXL=1010;
int wm[MAXN*MAXL],wa[MAXN*MAXL],wb[MAXN*MAXL],height[MAXN*MAXL],sa[MAXN*MAXL];
int *rank;
bool cmp(int *r,int a,int b,int len){
return r[a]==r[b]&&r[a+len]==r[b+len];
}
void da(int *data,int *sa,int n,int m){
int *x=wa,*y=wb,*t,i,j,p;
for(i=0;i<m;++i) wm[i]=0;
for(i=0;i<n;++i) ++wm[x[i]=data[i]];
for(i=1;i<m;++i) wm[i]+=wm[i-1];
for(i=n-1;i>=0;--i) sa[--wm[x[i]]]=i;
for(j=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<m;++i) wm[i]=0;
for(i=0;i<n;++i) ++wm[x[y[i]]];
for(i=1;i<m;++i) wm[i]+=wm[i-1];
for(i=n-1;i>=0;--i) sa[--wm[x[y[i]]]]=y[i];
for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
}
rank=x;
}
void calHeight(int *data,int *sa,int n){
for(int i=0,j,k=0;i<n;height[rank[i++]]=k)
for(k?--k:k,j=sa[rank[i]-1];data[i+k]==data[j+k];++k);
}
int len[MAXN],data[MAXL*MAXN];
int n,a[MAXL],t,cas=0;
char str[MAXL];
int nn;
bool vis[MAXN];
bool judge(int _len,int x){
t=0;
int flag,counte=0;
for(int i=1;i<=_len;++i){
if(height[i]>=x){
int posi1=sa[i-1],posi2=sa[i];
if(!(flag++)) memset(vis,false,sizeof vis);
for(int j=1;j<=n;++j){
if(posi1>=len[j-1]&&posi1<len[j]) counte+=vis[j-1]?0:1,vis[j-1]=true;
if(posi2>=len[j-1]&&posi2<len[j]) counte+=vis[j-1]?0:1,vis[j-1]=true;
if(counte>n/2) break;
}
}
else{
if(counte>n/2) a[t++]=i-1;
flag=counte=0;
}
}
if(counte>n/2) a[t++]=_len;
if(t){
nn=t;
return true;
}
else return false;
}
void solve(int _len){
int l=0,r=MAXL;
while(l<r){
int mid=l+(r-l+1)/2;
if(judge(_len,mid)) l=mid;
else r=mid-1;
}
if(!l) cout<<"?\n";
else{
for(int i=0;i<nn;++i){
int star=sa[a[i]];
for(int j=star;j<star+l;++j){
printf("%c",data[j]);
}
printf("\n");
}
}
}
int main(){
int p;
while(cin>>n,n){
p=len[0]=0;
for(int i=0;i<n;++i){
scanf("%s",str);
int temp_len=strlen(str);
len[i+1]=len[i]+temp_len+1;
for(int j=0;j<temp_len;++j){
data[p++]=str[j];
}
data[p++]=130+i;
}
if(cas++) cout<<endl;
if(n==1){
printf("%s\n",str);
continue;
}
data[p-1]=0;
da(data,sa,p,300);
calHeight(data,sa,p-1);
solve(p-1);
}
return 0;
}
相关文章推荐
- POJ 3261 Milk Patterns <后缀数组+二分>
- POJ 1743 Musical Theme <后缀数组+二分>
- Poj 3294 Life Forms(后缀数组+二分答案)
- poj 3294 不小于 k 个字符串中的最长子串(后缀数组+二分)
- POJ3294---Life Forms(后缀数组,二分+给后缀分组)
- Poj 3294 Life Forms (后缀数组 + 二分 + Hash)
- poj_3294 Life Forms(后缀数组+二分)
- POJ 3294 Life Forms(后缀数组+二分)
- POJ 3415 Common Substrings <后缀数组+单调栈>
- POJ 3693 Maximum Repetition Substring <后缀数组 + RMQ>
- poj 3294 Life Forms(后缀数组+二分)
- POJ 3294 Life Forms(后缀数组+二分答案)
- UVa 11107 (后缀数组 二分) Life Forms
- poj 3294 求多于k个字符串的最长公共子串的个数-------后缀数组+二分答案
- POJ 2774 Long Long Message <后缀数组(DC3)>
- POJ 3294 Life Forms (后缀数组,求出现在不少于k个字符串的最长子串)
- poj 3261 Milk Patterns (后缀数组+二分答案)
- poj 1743 Musical Theme (后缀数组+二分答案)
- 【POJ】3294 Life Forms 【后缀数组——求在超过一半串中出现的最长串】
- POJ 3294 (UVA 11107) Life Forms 后缀数组