UVA-11107 Life Forms(后缀数组)

Posted 真正的强者,从不埋怨黎明前的黑暗!!!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVA-11107 Life Forms(后缀数组)相关的知识,希望对你有一定的参考价值。

题目大意:给出n个字符串,找出所有最长的在超过一半的字符串中出现的子串。

题目分析:将所有的字符串连成一个,二分枚举长度,每次用O(n)的时间复杂度判断。连接字符串的时候中间添一个没有出现过的字符。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define mid (l+(r-l)/2)
# define LL long long

const int N=100000;

char s[N+105];
int SA[N+105],tSA[N+105];
int rk[N+105],cnt[N+105];
int height[N+105];
int flag[N+105];
bool vis[105];

int idx(char c)
{
	return c-‘a‘;
}

bool same(int i,int j,int k,int n)
{
	if(tSA[SA[i]]!=tSA[SA[j]]) return false;
	if(SA[i]+k>=n&&SA[j]+k>=n) return true;
	if(SA[i]+k<n&&SA[j]+k>=n) return false;
	if(SA[i]+k>=n&&SA[j]+k<n) return false;
	return tSA[SA[i]+k]==tSA[SA[j]+k];
}

void buildSA()
{
	int m=27;
	int n=strlen(s);
	for(int i=0;i<m;++i) cnt[i]=0;
	for(int i=0;i<n;++i) ++cnt[rk[i]=idx(s[i])];
	for(int i=1;i<m;++i) cnt[i]+=cnt[i-1];
	for(int i=n-1;i>=0;--i) SA[--cnt[rk[i]]]=i;
	
	for(int k=1;k<=n;k<<=1){
		int p=0;
		for(int i=n-k;i<n;++i) tSA[p++]=i;
		for(int i=0;i<n;++i) if(SA[i]>=k) tSA[p++]=SA[i]-k;
		
		for(int i=0;i<m;++i) cnt[i]=0;
		for(int i=0;i<n;++i) ++cnt[rk[tSA[i]]];
		for(int i=1;i<m;++i) cnt[i]+=cnt[i-1];
		for(int i=n-1;i>=0;--i) SA[--cnt[rk[tSA[i]]]]=tSA[i];
		
		swap(rk,tSA);
		rk[SA[0]]=0;
		p=1;
		for(int i=1;i<n;++i){
			if(same(i,i-1,k,n)) rk[SA[i]]=p-1;
			else rk[SA[i]]=p++;
		}
		if(p>=n) break;
		m=p;
	}
}

void getHeight()
{
	int n=strlen(s);
	for(int i=0;i<n;++i) rk[SA[i]]=i;
	int k=0;
	for(int i=0;i<n;++i){
		if(rk[i]==0)
			height[rk[i]]=k=0;
		else{
			if(k) --k;
			int j=SA[rk[i]-1];
			while(s[i+k]==s[j+k]) ++k;
			height[rk[i]]=k;
		}
	}
}

bool judge(int x,int n)
{
	memset(vis,false,sizeof(vis));
	vis[flag[SA[0]]]=true;
	int k=1;
	int len=strlen(s);
	for(int i=1;i<len;++i){
		if(k>n) return true;
		if(flag[SA[i]]==-1)
			break;
		if(height[i]<x){
			k=0;
			memset(vis,false,sizeof(vis));
		}
		if(!vis[flag[SA[i]]]){
			vis[flag[SA[i]]]=true;
			++k;
		}
	}
	return k>n;
}

int f(int l,int r,int n)
{
	++r;
	while(l<r){
		if(judge(mid,n)) l=mid+1;
		else r=mid;
	}
	return r-1;
}

bool ok(int i,int j)
{
	return flag[i]==flag[j];
}

void print(int p,int n)
{
	int len=strlen(s);
	memset(vis,false,sizeof(vis));
	int k=1;
	vis[flag[SA[0]]]=true;
	for(int i=1;i<len;++i){
		if(flag[SA[i]]==-1||height[i]<p){
			if(k>n&&ok(SA[i-1],SA[i-1]+p-1)){
				for(int j=SA[i-1];j<SA[i-1]+p;++j) printf("%c",s[j]);
				puts("");
			}
			if(flag[SA[i]]==-1) break;
			k=0;
			memset(vis,false,sizeof(vis));
		}
		if(!vis[flag[SA[i]]]){
			++k;
			vis[flag[SA[i]]]=true;
		}
	}
}

int main()
{
	int n;
	bool yy=false;
	while(scanf("%d",&n)&&n)
	{
		if(yy) puts("");
		yy=true;
		if(n==1){
			scanf("%s",s);
			printf("%s\n",s);
		}else{
			memset(flag,-1,sizeof(flag));
			s[0]=0;
			int high=0;
			for(int i=0;i<n;++i){
				int m=strlen(s);
				scanf("%s",s+m);
				int nm=strlen(s);
				for(int j=m;j<nm;++j) flag[j]=i;
				s[nm]=‘z‘+1;
				s[nm+1]=0;
				high=max(nm-m,high);
			}
			buildSA();
			getHeight();
			int len=f(0,high,n>>1);
			if(len<=0) printf("?\n");
			else print(len,n>>1);	
		}
	}
	return 0;
}

  

以上是关于UVA-11107 Life Forms(后缀数组)的主要内容,如果未能解决你的问题,请参考以下文章

UVA11107 训练指南Life Forms后缀数组

UVA-11107 Life Forms(求出现K次的子串,后缀数组+二分答案)

POJ3294:Life Forms(后缀数组)

POJ3294 Life Forms(二分+后缀数组)

POJ 3294 Life Forms(后缀数组+二分答案)

POJ3294Life Forms(后缀数组,二分)