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

7 篇文章 0 订阅
4 篇文章 0 订阅

题目:

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


思路:

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


代码:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值