首页 > 代码库 > BZOJ 1692 队列变换 贪心+后缀数组

BZOJ 1692 队列变换 贪心+后缀数组

题目大意:给定一个字符串,每次取头或者尾放在新字符串里,求字典序最小的新字符串

首先如果两边的字符不一样 那么肯定要选择小的放在新字符串里

但如果两边一样 比如CCBACC 肯定从尾取比较优 原因是CCA比CCB要小

于是我们把原串反写接在后面变成CCBACC@CCABCC 然后跑一遍后缀数组 每次就能O(1)比较两个子串的大小了

时间复杂度O(nlogn)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 60600
using namespace std;
int n;
char s[M];
int X[M],Y[M],sa[M],rank[M];
int sum[M],cnt[M],temp[M],tot;
inline char Get_Char()
{
	char c;
	do c=getchar(); while(c==' '||c=='\t'||c=='\n'||c=='\r');
	return c;
}
void Get_Rank()
{
	int i,j;
	for(i=1;i<=n;i++)
		sum[s[i]]++;
	for(i=1;i<=127;i++)
		sum[i]+=sum[i-1];
	for(i=1;i<=n;i++)
		sa[ sum[s[i]-1]+ ++cnt[s[i]] ]=i;
	for(i=1;i<=n;i++)
	{
		if(i==1||s[sa[i]]!=s[sa[i-1]])
			++tot;
		rank[sa[i]]=tot;
	}
}
void Radix_Sort(int key[],int order[])
{
	int i;
	for(i=0;i<=n;i++)
		sum[i]=cnt[i]=0;
	for(i=1;i<=n;i++)
		sum[key[i]]++;
	for(i=1;i<=n;i++)
		sum[i]+=sum[i-1];
	for(i=1;i<=n;i++)
		temp[ sum[key[order[i]]-1]+ ++cnt[key[order[i]]] ]=order[i];
	for(i=1;i<=n;i++)
		order[i]=temp[i];
}
void Prefix_Doubling()
{
	int i,j;
	Get_Rank();
	for(j=1;j<=n;j<<=1)
	{
		for(i=1;i<=n;i++)
		{
			X[i]=rank[i];
			Y[i]=i+j>n?0:rank[i+j];
			sa[i]=i;
		}
		Radix_Sort(Y,sa);
		Radix_Sort(X,sa);
		for(i=1,tot=0;i<=n;i++)
		{
			if( i==1 || X[sa[i]]!=X[sa[i-1]] || Y[sa[i]]!=Y[sa[i-1]] )
				++tot;
			rank[sa[i]]=tot;
		}
	}
}

int main()
{
	int i,j=0;
	cin>>n;
	s[n+1]='A'-1;
	for(i=1;i<=n;i++)
		s[n+1-i]=s[n+1+i]=Get_Char();
	n=n<<1|1;
	Prefix_Doubling();
	int l=1,r=n>>1;
	while(l<=r)
	{
		++j;
		if(s[l]<s[r]) putchar(s[l++]);
		else if(s[l]>s[r]) putchar(s[r--]);
		else if(rank[l]<rank[n+1-r]) putchar(s[l++]);
		else putchar(s[r--]);
		if(j%80==0) puts("");
	}
}


BZOJ 1692 队列变换 贪心+后缀数组