题目大意:
给定两个串 S 和 T,
∣
S
∣
>
=
∣
T
∣
|S| >= |T|
∣S∣>=∣T∣。
两个人博弈,每一轮操作,两人先后可以删掉 S 的首位或者末位。
当操作以后的串的长度等于
∣
T
∣
|T|
∣T∣时,游戏停止。
如果停止时
S
剩
下
的
串
S 剩下的串
S剩下的串
=
T
=T
=T,则后手获胜,否则先手获胜。
问两人均采取最优策略下谁赢
1
<
=
t
<
=
10
1<=t<=10
1<=t<=10
1
<
=
∣
T
∣
<
=
∣
S
∣
<
=
100000
1<=|T|<=|S|<=100000
1<=∣T∣<=∣S∣<=100000
分析:
考虑简化表示状态
设左边已经删掉了 L 个字符,右边已经删掉了 R 个字符
那么用 R-L 来表示当前状态,可以用 KMP 求出有哪些目标状态
一开始 R-L 为 0,每一次操作可以让它+1 或者-1,双方轮流操作,总共|S|-|T|次操
作,双方都用最优策略看最后是否能到达目标状态
显然所有的目标状态奇偶性相同
当|S|-|T|为奇数时,最后一次操作是先手做,它肯定往不是目标状态走,那么一个
位置在最后一次操作前是目标状态当且仅当它+1 它-1 都是目标状态
现在就全部转化成|S|-|T|为偶数的情况
假如 0 是目标状态,那么显然后手会赢,因为无论先手往哪里走后手都可以把他拉
回来
如果 0 不是,并且-2 和 2 不全是,那么先手一定会朝不是的那一边走,后手无论
如何都没有办法将它拉回到是的那一边了
因此后手会赢当且仅当 0 是目标状态或者-2 和 2 都是目标状态
否则都是先手赢
利用KMP求出目标状态的所有位置然后判断。
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#define N 100005
using namespace std;
int begind[2][N], next[N], Q;
char S[N], T[N];
int main()
{
freopen("vanusee.in", "r", stdin);
freopen("vanusee.out", "w", stdout);
scanf("%d", &Q);
while (Q--)
{
scanf("%s", S + 1); int len1 = strlen(S + 1);
scanf("%s", T + 1); int len2 = strlen(T + 1);
memset(begind, 0, sizeof(begind));
int j = 0, tot = 0, orz = (len1 - len2) % 2;
for (int i = 2; i <= len2; i++)
{
while (j && T[i] != T[j + 1]) j = next[j];
if (T[i] == T[j + 1]) j++;
next[i] = j;
}
j = 0;
for (int i = 1; i <= len1; i++)
{
while (j && S[i] != T[j + 1]) j = next[j];
if (S[i] == T[j + 1]) j++;
if (j == len2)
begind[orz][++tot] = (len1 - i) - (i - len2), j = next[j];
}
if (orz)
{
sort(begind[orz] + 1, begind[orz] + tot + 1);
for (int i = 2; i <= tot; i++)
if (begind[orz][i] - begind[orz][i - 1] == 2) begind[orz][i - 1] = begind[orz][i] - 1;
tot--;
}
bool xx = 0, yy = 0, xy = 0;
for (int i = 1; i <= tot; i++)
{
if (begind[orz][i] == 2) xx = 1;
if (begind[orz][i] == -2) yy = 1;
if (begind[orz][i] == 0) xy = 1;
}
if (xy || (xx && yy)) printf("pty\n"); else printf("cqf\n");
}
return 0;
}