p1940 [usaco2007dec_gold]队列变换——后缀数组模板题

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/84891869

题目

描述 Description
  FJ打算带他的N(1 <= N <= 30,000)头奶牛去参加一年一度的“全美农场主
大奖赛”。在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们
从裁判席前依次走过。

今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所
有队伍中奶牛名字的首字母取出,按它们对应奶牛在队伍中的次序排成一列(比
如说,如果FJ带去的奶牛依次为Bessie、Sylvia、Dora,登记人员就把这支队伍
登记为BSD)。登记结束后,组委会将所有队伍的登记名称按字典序升序排列,
就得到了他们的出场顺序。

FJ最近有一大堆事情,因此他不打算在这个比赛上浪费过多的时间,也就是
说,他想尽可能早地出场。于是,他打算把奶牛们预先设计好的队型重新调整一
下。

FJ的调整方法是这样的:每次,他在原来队列的首端或是尾端牵出一头奶牛
,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有
奶牛都被插到了新的队列里。这样得到的队列,就是FJ拉去登记的最终的奶牛队
列。

接下来的事情就交给你了:对于给定的奶牛们的初始位置,计算出按照FJ的
调整规则所可能得到的字典序最小的队列。

输入格式 Input Format
第1行: 一个整数:N

  • 第2…N+1行: 第i+1行仅有1个’A’…'Z’中的字母,表示队列中从前往后数第i
           头奶牛名字的首字母

输出格式 Output Format
第1…??行: 输出FJ所能得到的字典序最小的队列。每行(除了最后一行)输
       出恰好80个’A’…'Z’中的字母,表示新队列中每头奶牛姓名的首
       字母

样例输入 Sample Input

6
A
C
D
B
C
B

输入说明:

FJ有6头顺次排好队的奶牛:ACDBCB

样例输出 Sample Output

ABCBCD

输出说明:

操作数 原队列 新队列
#1 ACDBCB
#2 CDBCB A
#3 CDBC AB
#4 CD ABC
#5 CD ABCB
#6 D ABCBC
#7 ABCBCD

时间限制 Time Limitation
1s
注释 Hint
1s
来源 Source
usaco 2007 dec gold bclgold

题解

我第一次看到这道题,想到了贪心,(虽然题目上是后缀数组),但是昨天听过L老师讲课,我就想着先打个暴力,然后再去学习后缀数组,写个一题多解:
这个做法就是贪心,从两头分别走,小的就进入队列,若是两头相等,则整体考虑,取最优的一侧,进入队列。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e4+10;
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar();} 
	while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
	return num*f;
}
char ch[maxn];//原始数据
char q[maxn];//手写队列
int main()
{
	int n=read();
	for (int i=1;i<=n;++i)
		cin>>ch[i];
	int l=1,r=n,tail=0;//二分
	while (l<=r)
	{
		if (ch[l]<ch[r])
			q[++tail]=ch[l++];
		else if (ch[l]==ch[r])
		{
			int i=l,j=r;
			while (i!=j && ch[i]==ch[j]) ++i,--j;
			if (ch[i]>ch[j]) q[++tail]=ch[r--];
			else q[++tail]=ch[l++];
		}	
		else
			q[++tail]=ch[r--];
	}
	
	int m=0;
	while (m!=n)
	{
		for (int i=1;i<=80;++i)
			if (m!=n)
				printf("%c",q[++m]);
		printf("\n");
	}
	return 0;
}

结果

#01: Accepted (0ms, 568KB)
#02: Accepted (0ms, 568KB)
#03: Accepted (7ms, 568KB)
#04: Accepted (15ms, 568KB)
#05: Accepted (15ms, 568KB)
#06: Accepted (15ms, 568KB)
#07: Accepted (15ms, 568KB)
#08: Accepted (11ms, 568KB)
#09: Accepted (78ms, 568KB)
#10: Accepted (734ms, 568KB)
#11: Accepted (93ms, 568KB)
#12: Accepted (406ms, 568KB)
#13: Accepted (93ms, 568KB)
#14: Accepted (578ms, 568KB)
#15: Accepted (187ms, 568KB)
#16: Accepted (78ms, 568KB)
#17: Accepted (171ms, 568KB)
#18: Accepted (375ms, 568KB)
#19: Accepted (156ms, 568KB)
#20: Accepted (625ms, 568KB)

Accepted / 100 / 3660ms / 11360KB

好啦,去学后缀数组了,学完再继续码博客。

#include<bits/stdc++.h>
#define up(i,j,n) for(int i=j;i<=n;i++)
#define down(i,j,n) for(int i=j;i>=n;i--)
using namespace std;
typedef long long ll;
const int maxn=6e4+100;
const int inf=1e9;
inline int read()
{
	int f=1,num=0;
	char ch=getchar();
	while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); }
	while (isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48), ch=getchar();
	return num*f;
}
int a[maxn],x[maxn],y[maxn];
int c[maxn],sa[maxn],que[maxn];

inline void build_sa(int n,int m)
{
	memset(c,0,sizeof(c));
	up(i,0,n-1) c[x[i]=a[i]]++;
	up(i,1,m-1) c[i]+=c[i-1];
	down(i,n-1,0) sa[--c[x[i]]]=i;//初始化 
	for(int k=1;k<=n;k<<=1)
	{
		int p=0;
		up(i,n-k,n-1) y[p++]=i;
		up(i,0,n-1)
			if (sa[i]>=k) y[p++]=sa[i]-k;//根据第二关键字排序 
		memset(c,0,sizeof(c));
		up(i,0,n-1) c[x[y[i]]]++;
		up(i,1,m-1) c[i]+=c[i-1];
		down(i,n-1,0) sa[--c[x[y[i]]]]=y[i];//根据第一关键字排序 
		swap(x,y);
		p=1,x[sa[0]]=0;
		up(i,1,n-1) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
		if (p>=n) break;
		m=p;
	}
}
int main()
{
	int n=read();
	int tot=2*n+1;
	up(i,0,n-1)
	{
		char ch=getchar();
		while (ch<'A'||ch>'Z') ch=getchar();
		a[i]=ch-'A'+1;
	}
	a[n]=a[tot]=0;
	up(i,1,n) a[n+i]=a[n-i];
	build_sa(tot,27);
	up(i,0,tot-1) que[sa[i]]=i;
	int l=0,r=n-1,cnt=0;
	while (l<=r)
	{
		if (que[l]<que[2*n-r]) printf("%c",a[l++]+'A'-1);
		else printf("%c",a[r--]+'A'-1);
		cnt++;
		if (cnt%80==0) printf("\n");
	}
	if (cnt%80==0) printf("\n");
	return 0;
}

结果

#01: Accepted (7ms, 3008KB)
#02: Accepted (15ms, 3008KB)
#03: Accepted (11ms, 3008KB)
#04: Accepted (15ms, 3008KB)
#05: Accepted (11ms, 3008KB)
#06: Accepted (31ms, 3008KB)
#07: Accepted (15ms, 3008KB)
#08: Accepted (11ms, 3008KB)
#09: Accepted (93ms, 3008KB)
#10: Accepted (93ms, 3008KB)
#11: Accepted (93ms, 3008KB)
#12: Accepted (93ms, 3008KB)
#13: Accepted (93ms, 3008KB)
#14: Accepted (93ms, 3008KB)
#15: Accepted (78ms, 3008KB)
#16: Accepted (93ms, 3008KB)
#17: Accepted (93ms, 3008KB)
#18: Accepted (78ms, 3008KB)
#19: Accepted (93ms, 3008KB)
#20: Accepted (93ms, 3008KB)

Accepted / 100 / 1214ms / 60160KB

很明显,后缀数组跑得快。但是占空间大。。。。。。。。。。

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/84891869