题意:给你n个字符串,所有的字符串的长度加起来不超过10^5,两人来构造一个字符串,首先该字符串是为空,然后两人轮流选一个字母加在该字符串的尾部,要求两人构成的字符串是n个字符串中某个字符串的子串,没法继续选字母者败。 给你一个K,表示将该游戏进行K次,其中第一次游戏First先手,后面的k-1次的先手为第上次游戏的败者
例如,第一次First胜,则第二次游戏Second先手
求第K次游戏的胜者
方法:
将n个字符串存在字典树,然后遍历整棵树,其中每个结点状态的可能性有四种 1(表示改点必胜),2(表示改点必败),3(表示改点想败就败,想胜就胜),4(表示改点可能败,也可能胜,胜负由对手控制)
遍历树的过程时:
if(改点为树的叶子节点) ,改点状态为2 (因为无法再选择字母)
else if 某点的子节点中有状态为4的节点,则改点的状态为3 (因为只要在该点的人走到4,就可以控制对手的胜负,所有改点的状态为3)
当改点不是叶子节点且改点的字节点没有状态为4的点时:
if 改点的子节点有状态为1还有状态为2的子节点,则改点的状态为3(如果还有子节点有状态为3的节点,该点的状态也是3,因为只要该点不朝状态为3的点那边走即可)
else if 该点的子节点有状态为1且没有状态为2的子节点,则该点的状态为2 (如果还有子节点有状态为3的节点,该点也是必败,因为如果到达该点的人朝状态为3走,表示该
人该把想赢(想输的话走状态为1的子节点就可以了),但是朝3走之后,另一个人还是可以让他绝对输,所以该点状态为2)
else if 改点没有状态为1的点有状态为2的子节点,则改点的状态为1(理由同上)
else if 改点的子节点只有状态为3的子节点,改点的状态为4 (因为只要该点走到3,对手就能够想赢就赢,想输就输)
知道根节点的状态ans后进行判断:
若ans= 1,可得K把游戏中,First和Second两人轮流赢,则判断K的奇偶性就可知道第K把谁用
若ans= 2,可得K把游戏中,First每把都是先手,每把都输,则结果为Second胜
若ans=3,可得First第一把就可控制胜负,所有他只要一直控制K-1把让自己输,第K把赢即可
若ans=4, 可得胜负由对手控制,同上理可得,结果为Second胜
代码:
#include <cstdio>
#include <cstring>
#define maxn 100005
int trie[maxn][27];
char ch[maxn];
int dfs(int n)
{
if(n== -1)
return 1;
int f1= 0, f2= 0, f3= 0, f4= 0;
int sum= 0;
for(int i= 1; i<= 26; i++)
{
if(trie[n][i]== -1)continue;
sum++;
int res= dfs(trie[n][i]);
if(res== 1)
f1= 1;
else if(res== 2)
f2= 1;
else if(res== 3)
f3= 1;
else if(res== 4)
f4= 1;
}
if(sum== 0)
return 2;
if(f4)
return 3;
if(f1)
{
if(f2)
return 3;//可胜可负
else
return 2;
}
else if(f2)
return 1;
else
return 4;
}
int main()
{
int n, k;
while(scanf("%d %d",&n,&k)!=EOF)
{
memset(trie, -1, sizeof(trie));
int tot= 0; //树中节点的个数
for(int i= 1; i<= n; i++)
{
scanf("%s",ch+1);
int len= strlen(ch+1);
int tt= 0; //当前的节点
for(int j= 1; j<= len; j++)
{
int k= ch[j]- 'a'+1;
if(trie[tt][k]== -1)
{
trie[tt][k]= ++tot;
tt= tot;
}
else
tt= trie[tt][k];
}
}
int ans= dfs(0);
if(ans== 1)
{
if(k&1)
printf("First\n");
else
printf("Second\n");
}
else if(ans== 2 || ans== 4)
printf("Second\n");
else if(ans== 3)
printf("First\n");
}
return 0;
}