字符串游戏-博弈论-trie
Description
从前有个游戏。游戏分为 k 轮。
给定一个由小写英文字母组成的字符串的集合 S,
在每轮游戏开始时,双方会得到一个空的字符串,
然后两人轮流在该串的末尾添加字符,并且需要保证新的字符串是 S 中某个串的前缀,直到有一方不能操作,则不能操作的一方输掉这一轮。
新的一轮由上一轮输的人先手,最后一轮赢的人获得游戏胜利。
假定双方都采取最优策略,求第一轮先手的一方能否获胜。
Input
输入包含多组数据。
每组数据的第一行包含两个整数 n,k,分别表示字符串的数量和游戏的轮数。
接下来 n 行,每行一个由小写英文字母组成的字符串。
Output
对于每组数据输出一行,若先手能获胜输出 HY wins!,否则输出 Teacher wins!
Sample Input
2 3
a
b
3 1
a
b
c
Sample Output
HY wins!
HY wins!
HINT
1 ≤ n ≤ 1e5,1 ≤ k ≤ 1e9,保证所有字符串长度不超过 1e5,数据组数不超过 10。
思路
- 实现:f数组:两人都想赢时是否有必赢策略,g数组:两人都想输时是否有必输策略
- 先手无必胜策略,则先手在k轮游戏中都是先手,必败;
- 先手有必胜策略,无必败策略,则每轮游戏双方都会交换先后手,如果进行了奇数轮游戏,则先手必胜,否则先手必败;
- 先手既有必胜策略也有必败策略,则先手可以在前k-1轮游戏中使用必败策略,在最后一轮使用必胜策略,因此先手必胜。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,k,cnt,f[1000005],g[1000005];
char s[100005];
struct fdfdfd{
int ch[27],flag;
void init(){memset(ch,0,sizeof(ch)); flag=0;}
}e[1000005];
void insert(char a[],int len)
{
int root=1;
for(int i=0;i<len;++i)
{
int j=a[i]-'a'+1; e[root].flag=1;
if(!e[root].ch[j]) {e[root].ch[j]=++cnt; e[cnt].init();}
root=e[root].ch[j];
}
}
void dfs(int x)
{
if(!e[x].flag) {g[x]=1; f[x]=0; return;}
f[x]=0; g[x]=0;
for(int i=1;i<=26;++i)
{
if(!e[x].ch[i]) continue;
dfs(e[x].ch[i]);
if(!f[e[x].ch[i]]) f[x]=1;
if(!g[e[x].ch[i]]) g[x]=1;
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
cnt=1; e[1].init(); memset(f,0,sizeof(f)); memset(g,0,sizeof(g));
for(int i=1;i<=n;++i) scanf("%s",&s),insert(s,strlen(s));
dfs(1);
if((f[1]&&g[1])||(f[1]&&(k%2==1))) puts("HY wins!");
else puts("Teacher wins!");
}
return 0;
}