【JZOJ1247】【洛谷P2870】队列变换【hash】【二分】【贪心】

题目:

题目链接:https://www.luogu.org/problem/P2870
FJ打算带他的N(1 <= N <= 30,000)头奶牛去参加一年一度的“全美农场主大奖赛”。在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过。
今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所有队伍中奶牛名字的首字母取出,按它们对应奶牛在队伍中的次序排成一列(比如说,如果FJ带去的奶牛依次为Bessie、Sylvia、Dora,登记人员就把这支队伍登记为BSD)。登记结束后,组委会将所有队伍的登记名称按字典序升序排列,就得到了他们的出场顺序。
FJ最近有一大堆事情,因此他不打算在这个比赛上浪费过多的时间,也就是说,他想尽可能早地出场。于是,他打算把奶牛们预先设计好的队型重新调整一下。
FJ的调整方法是这样的:每次,他在原来队列的首端或是尾端牵出一头奶牛,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有奶牛都被插到了新的队列里。这样得到的队列,就是FJ拉去登记的最终的奶牛队列。
接下来的事情就交给你了:对于给定的奶牛们的初始位置,计算出按照FJ的调整规则所可能得到的字典序最小的队列。


思路:

如果现在头和尾的字符不同,那么肯定选小的。
如果相同,那么肯定将第二项和倒数第二项进行比较,选择较小的那一边。
但是这样的时间复杂度是O(n2)O(n^2)的。
不难发现,如果这个字符串的前ii项和后ii项相同,那么前i1i-1项和后i1i-1项也一定相同。所以这个满足单调性,我们可以二分它相同的长度,然后每次用hashhashcheckcheck
得出前后相同的最大程度lenlen后,我们比较第len+1len+1项和倒数第len+1len+1项即可。这样的时间复杂度就是O(nlogn)O(n\log n)


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;

const int N=30010;
const ull base=13331;
ull hash[3][N],p[N];
int n,a[N];
char ch;

void solve()
{
	for (int i=1,j=n,cnt=0;i<=j;)
	{
		int l=1,r=j-i+1,mid;
		while (l<=r)
		{
			mid=(l+r)>>1;
			if (hash[1][i+mid-1]-hash[1][i-1]*p[mid]==hash[2][j-mid+1]-hash[2][j+1]*p[mid]) l=mid+1;
				else r=mid-1;
		}
		if (a[i+r]>a[j-r]) putchar(a[j--]+'A'-1);
			else putchar(a[i++]+'A'-1);
		cnt++;
		if (!(cnt%80)) putchar(10);
	}
}

int main()
{
	//freopen("ans.txt","w",stdout);
	scanf("%d",&n);
	p[0]=1;
	for (int i=1;i<=n;i++)
	{
		while (ch=getchar()) if (ch>='A' && ch<='Z') break;
		a[i]=ch-'A'+1;
		p[i]=p[i-1]*base;
	}
	for (int i=1;i<=n;i++)
		hash[1][i]=hash[1][i-1]*base+a[i];
	for (int i=n;i>=1;i--)
		hash[2][i]=hash[2][i+1]*base+a[i];
	solve();
	return 0;
}
posted @ 2019-11-08 14:37  全OI最菜  阅读(114)  评论(0编辑  收藏  举报