题目链接:FZU_2275 Game
题意
Alice 可以对数字 A 操作,Bob 可以对数字 B 操作,有两种操作:
1、反转,如 1234 反转为 4321;
2、除以 10 向下取整,如 1234/10=123;
Alice 先手,轮流操作,每次操作能选择 1 或 2 操作一次,如果能在有限步内达到 A==B,则 Alice 获胜,否则 Bob 获胜。给出 A,B,问谁能获胜?
思路
因为能反转也能去掉末尾一位,容易想到 Bob 的最优策略是不要改变 B 串的长度,那么当 A 的某个子串为 B 或 B的反转,则 Alice 获胜,否则 Bob 获胜,这就是一个 kmp 判断主串能否找到模式串的匹配的问题。要特判 B 为 0 的时候,Alice 必胜。
整理了 kmp 模板在代码中,时间复杂度 $O(n+m)$ 。
代码实现
#include <cstdio> #include <cstring> #include <iostream> using std::swap; const int N = 100010; char s1[N], s2[N]; // s2为模式串,s1为主串 int nex[N]; void get_next(int n2) { nex[1] = nex[0] = 0; int k = 0; for (int i = 2; i < n2; i++) { for (; k && s2[k] != s2[i-1]; k = nex[k]); if (s2[k] == s2[i-1]) k++; nex[i] = k; } } /* // count版kmp,返回s2在s1中出现的次数(可叠加) int kmp_count(int n1, int n2) { int j = 0, sum = 0; for (int i = 0; i < n1; i++) { while (s1[i] != s2[j] && j > 0) { j = nex[j-1]; } if (s1[i] == s2[j]) j++; if (j == n2) { sum++; j = nex[j-1]; } } return sum; } */ // 返回是否能在s1中找到s2的匹配 bool kmp_match(int n1, int n2) { int q = 0, s = 0; while (s < n1) { for (q = nex[q]; q < n2 && s2[q] == s1[s]; q++, s++); if (q == 0) s++; else if (q == n2) return true; // return s - n2 + 1; 返回匹配的起始位置 } return false; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%s %s", s1, s2); int n1 = strlen(s1), n2 = strlen(s2); if (s2[0] == '0' && n2 == 1) puts("Alice"); else if (n1 < n2) puts("Bob"); else { get_next(n2); if (kmp_match(n1, n2)) { puts("Alice"); continue; } for (int i = 0; i < n2 / 2; i++) swap(s2[i], s2[n2-1-i]); get_next(n2); if (kmp_match(n1, n2)) { puts("Alice"); continue; } puts("Bob"); } } return 0; }