UVALive 7902 - 后缀数组

题解思路:

考虑将所有的串都连起来,并且每个串本来的最后一个位置,这样防止之后考虑排名的时候会越界,设第一个字符串的长度为len,那么我们为0-len内的位置按排名从大到小枚举出来,这样就只考虑长度了不用再考虑大小了。那么我们以枚举的这个排名位置前后去扫描得出最大相同长度,因为要找到位置是在len之外。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 3e5+10,mod = 1e9+7;
char str[mx],st[mx];  
int c[mx],wa[mx],wb[mx],sa[mx],height[mx],ran[mx],pos[mx];  
void sort_sa(int m,int n){  
    int *x=wa,*y=wb,p=0;  
    for(int i=0;i<m;i++)  c[i]=0;  
    for(int i=0;i<n;i++)  c[x[i]=str[i]]++;  
    for(int i=1;i<m;i++)  c[i]+=c[i-1];  
    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;  
    for(int k=1;p<n;k<<=1,m=p){  
        p=0;  
        for(int i=n-k;i<n;i++) y[p++]=i;//按个位排序   
        for(int i=0;i<n;i++)  if(sa[i]>=k) y[p++]=sa[i]-k;  
        for(int i=0;i<m;i++) c[i]=0;  
        for(int i=0;i<n;i++) c[x[y[i]]]++;  
        for(int i=1;i<m;i++) c[i]+=c[i-1];  
        for(int i=n-1;i>=0;i--)  sa[--c[x[y[i]]]]=y[i];   
        swap(x,y);x[sa[0]] = 0,p = 1;  
        for(int i=1;i<n;i++)  
        if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]) x[sa[i]]=p-1;  
        else x[sa[i]]=p++;  
    }  
}  
void get_height(){  
    int k=0,len=strlen(str);  
    for(int i=1;i<=len;i++) ran[sa[i]]=i;  
    for(int i=0;i<len;i++){  
        if(k) k--;  
        int j=sa[ran[i]-1];  
        while(str[j+k]==str[i+k]) k++;  
        height[ran[i]]=k;  
    }  
}  
int len,lens,n,m,back[mx];
struct node{
	int pos,big;
	bool operator < (node A)const
	{
		return big < A.big;
	}
}s[mx];
int main()
{
	int t,cas = 1;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		scanf("%s",str);
		lens = len = strlen(str);
		for(int i=1;i<n;i++){
			scanf("%s",st);
			int lon = strlen(st);
			for(int j=0;j<lon;j++) str[lens+j] = st[j],back[lens+j] = lens+lon;
			lens += lon;
		}
		str[lens] = 0;
		sort_sa(150,lens+1); 
		get_height();  
		printf("Case #%d: ",cas++);
		for(int i=0;i<len;i++){
			s[i].pos = i;
			s[i].big = ran[i];
		}
		sort(s,s+len);
		int ra,maxx,minn,po,ta,flag = 1,minlen = 1e9;
		for(int i=0;i<len;i++){
			ra = s[i].big,minn = 1e9,ta = ra+1;
			maxx = 0;
			while(ta<=lens&&height[ta]){
				minn = min(minn,height[ta]);
				po = sa[ta];
				if(po>=len){
					if(po+minn>back[po]) maxx = max(maxx,back[po]-po);
					else{
						maxx = max(maxx,minn);
				    	break;
					}
				}
				ta++;
			}minn = 1e9;
			while(ra&&height[ra]){
				minn = min(minn,height[ra]);
				po = sa[ra-1];
				if(po>=len){
					if(po+minn>back[po]) maxx = max(maxx,back[po]-po);
					else{
						maxx = max(maxx,minn);
				    	break;
					}
				}
				ra--;
			}
			if(maxx<len-s[i].pos){
				if(maxx<minlen){
					for(int j=0;j<=maxx;j++) st[j] = str[s[i].pos+j];
					st[maxx+1] = 0;
					minlen = maxx;
					flag = 0;
				}
			}
		}
		if(flag) puts("Impossible");
		else puts(st);
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值