【博弈】[牛客练习赛 17] F 玩游戏

Description

链接:https://www.nowcoder.com/acm/contest/109/F
来源:牛客网

给定两个串S和T,|S| >= |T|。
alice和bob轮流操作串S,bob先手。
对于每次操作,alice或bob会选择删掉S的第一位或最后一位。
当操作以后的串的长度等于|T|时,游戏停止。
如果停止时的串=T,则alice获胜,否则bob获胜。
问在alice和bob均采取最优策略的情况下,谁赢?

t组数据
1<=|T|<=|S|<=500000,t<=1000
字符串总长度不超过1000000

Solution

考虑简化表示状态

设左边已经删掉了L个数,右边已经删掉了R个数
那么用R-L来表示当前状态,可以用KMP求出有哪些目标状态

一开始R-L为0,每一次操作可以让它+1或者-1,双方轮流操作,总共|S|-|T|次操作,双方都用最优策略看最后是否能到达目标状态

显然所有的目标状态奇偶性相同

当|S|-|T|为奇数时,最后一次操作是Bob做,它肯定往不是目标状态走,那么一个位置在最后一次操作前是目标状态当且仅当它+1它-1都是目标状态

现在就全部转化成|S|-|T|为偶数的情况

假如0是目标状态,那么显然Alice会赢,无论Bob往哪里走Alice后手都可以把他拉回来

如果0不是,并且-2和2不全是,那么Bob一定会朝不是的那一边走,Alice无论如何都没有办法将它拉回到是的那一边了

因此Alice会赢当且仅当0是目标状态或者-2和2都是目标状态
否则都是Bob赢

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 500005
using namespace std;
int n,m,t,cnt[N],nx[N],ct[N];
char st[N],ch[N];
void kmp()
{
	n=strlen(st+1),m=strlen(ch+1);
	int j=0;
	fo(i,2,m)
	{
		while(j&&ch[i]!=ch[j+1]) j=nx[j];
		if(ch[i]==ch[j+1]) j++;
		nx[i]=j;
	}
	j=0;
	fo(i,1,n)
	{
		while(j&&st[i]!=ch[j+1]) j=nx[j];
		if(st[i]==ch[j+1]) j++;
		if(j==m)
		{
			if((n-m)%2==0) cnt[++cnt[0]]=n-i-(i-m);
			else ct[++ct[0]]=n-i-(i-m);
			j=nx[j];
		}
	}
}
int main()
{
	cin>>t;
	while(t--)
	{
		scanf("\n%s",st+1);
		scanf("%s",ch+1);
		cnt[0]=0,ct[0]=0;
		kmp();
		if((n-m)%2!=0)
		{
			sort(ct+1,ct+ct[0]+1);
			fo(i,2,ct[0]) if(ct[i]-ct[i-1]==2) cnt[++cnt[0]]=ct[i]-1;  
		}
		bool pd=0,pd1=0,pd2=0;
		fo(i,1,cnt[0])
		{
			if(cnt[i]==0) pd=1;
			if(cnt[i]==2) pd1=1;
			if(cnt[i]==-2) pd2=1;
		}
		if(pd||(pd1&&pd2)) printf("Alice\n");
		else printf("Bob\n");
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值