POJ 3581 Sequence ——后缀数组 最小表示法

【题目分析】

    一见到题目,就有了一个显而易见obviously的想法。只需要每次找到倒过来最小的那一个字符串翻转就可以了。

    然而事情并不是这样的,比如说505023这样一个字符串,如果翻转了成为320505.

    最小的后缀是05,那么得到的字符串是055023,然而最小的结果是全部翻转,为050523.

    因为我们没有考虑到翻转后的字符和未翻转的字符有可能会连接起来,我们并没有考虑到连接部分对答案的影响。

    这里我们用最小表示的方法来做这道题目,每次操作完成之后,都需要把翻转后的串复制两遍,这样的话,我们找到在复制的第一遍中的最小的后缀输出。

    这样子我们就可以考虑到他本身的翻转和不翻转的部分组成的字符串的大小。

    上面的方法就是后缀数组,复制两次的方法其实就是为了求最小表示的开头,为O(nlogn)。

    所以我们可以直接用最小表示法来做,而且不需要复制,最小表示法是O(n)的,直接扫一遍就可以得到最小表示的起始位置,然后输出即可。

    这样就有了两种方法。

    然而人比较懒,练练后缀数组好了。

    注意边界的条件(第一次翻转最少剩下两个字符,为后面两次翻转留下空间)。

【代码】

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>

#include <map>
#include <set>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

#define maxn 800005
#define inf 0x3f3f3f3f
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)

void Finout()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    #endif
}

int Getint()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int n,a[maxn],b[maxn],top;

struct SuffixArray{
	int s[maxn];
	int tmp[maxn],cnt[maxn],sa[maxn],rk[maxn],h[maxn];
	void build(int n,int m)
	{
		int i,j,k;n++;
		F(i,0,2*n+5) tmp[i]=sa[i]=rk[i]=h[i]=0;
		F(i,0,m-1) cnt[i]=0;
		F(i,0,n-1) cnt[rk[i]=s[i]]++;
		F(i,1,m-1) cnt[i]+=cnt[i-1];
		F(i,0,n-1) sa[--cnt[rk[i]]]=i;
//		cout<<"       ";F(i,0,n) cout<<b[s[i]]<<" ";cout<<endl;
		for (k=1;k<=n;k<<=1)
		{
			F(i,0,n-1)
			{
				j=sa[i]-k;
				if (j<0) j+=n;
				tmp[cnt[rk[j]]++]=j;
			}
			sa[tmp[cnt[0]=0]]=j=0;
			F(i,1,n-1)
			{
				if (rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k]) cnt[++j]=i;
				sa[tmp[i]]=j;
			}
			memcpy(rk,sa,n*sizeof(int));
			memcpy(sa,tmp,n*sizeof(int));
			if (j>=n-1) break;
		}
//		cout<<"SA     ";F(i,0,n) cout<<sa[i]<<" ";cout<<endl;
//		for (j=rk[h[i=k=0]=0];i<n-1;++i,++k)
//			while (~k&&s[i]!=s[sa[j-1]+k]) h[j]=k--,j=rk[sa[j]+1];
//		cout<<"height ";F(i,0,n) cout<<h[i]<<" "; cout<<endl;
	}
}arr;

int main()
{
	Finout();
	n=Getint();
	F(i,0,n-1) a[i]=Getint(),b[i]=a[i];
	b[n]=-inf;
	sort(b,b+n+1);
	top=unique(b,b+n+1)-b;
	F(i,0,n-1) a[i]=lower_bound(b,b+top,a[i])-b;
	F(i,0,n-1) arr.s[n-1-i]=a[i];
	arr.s[n]=0;
	arr.build(n-1,2005);
//	cout<<"build over"<<endl;
	int tmp=0;
	while (arr.sa[tmp]<2||arr.sa[tmp]>=n) tmp++;
	F(i,arr.sa[tmp],n-1) cout<<b[arr.s[i]]<<endl;// cout<<endl;
	n-=n-arr.sa[tmp];
	F(i,0,n-1) arr.s[i+n]=arr.s[i];
	arr.s[2*n]=0;
	arr.build(2*n-1,2005);
	tmp=0; while (arr.sa[tmp]<1||arr.sa[tmp]>=n) tmp++;
	F(i,arr.sa[tmp],n-1) cout<<b[arr.s[i]]<<endl; //cout<<endl;
	F(i,0,arr.sa[tmp]-1) cout<<b[arr.s[i]]<<endl; //cout<<endl;
}

  

posted @ 2017-01-23 18:42  SfailSth  阅读(157)  评论(0编辑  收藏  举报