题意
给出一个串
t
t
和个
t
t
的子串。两个人轮流操作,每次可以选择一个串
s[i]
s
[
i
]
,然后在
s[i]
s
[
i
]
的最后添上一个字符串,满足得到的新串仍然是t的子串。不能操作者输,问先手必胜还是后手必胜。
t≤105,∑|s[i]|≤3∗107
t
≤
10
5
,
∑
|
s
[
i
]
|
≤
3
∗
10
7
分析
楼教是男人就过8题中最简单的一道题,怎么说我也已经是
18
1
8
个男人了。
先把后缀自动机建出来,在一个串后面添加字符等价于把其在sam上的对应节点在DAG上后移一位。
那么我们可以先预处理出后缀自动机上每个点的SG值,然后每个字符串的SG值异或和即为答案。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
const int N=200005;
int sz,last,mx[N],ch[N][26],a[N],deg[N],fa[N],sg[N],t[N];
char str[30000005];
std::queue<int> que;
void extend(int x)
{
int p,q,np,nq;
p=last;last=np=++sz;mx[np]=mx[p]+1;
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void pre()
{
for (int i=1;i<=sz;i++) deg[i]=sg[i]=0;
for (int i=1;i<=sz;i++)
for (int j=0;j<26;j++)
if (ch[i][j]) deg[ch[i][j]]++;
for (int i=1;i<=sz;i++) if (!deg[i]) que.push(i);
int tot=0;
while (!que.empty())
{
int x=que.front();que.pop();
a[++tot]=x;
for (int i=0;i<26;i++)
if (ch[x][i])
{
deg[ch[x][i]]--;
if (!deg[ch[x][i]]) que.push(ch[x][i]);
}
}
for (int i=sz;i>=1;i--)
{
int x=a[i];
for (int j=0;j<26;j++)
if (ch[x][j]) t[sg[ch[x][j]]]=1;
for (int j=0;;j++) if (!t[j]) {sg[x]=j;break;}
for (int j=0;j<26;j++)
if (ch[x][j]) t[sg[ch[x][j]]]=0;
}
}
int main()
{
while (scanf("%s",str)!=EOF)
{
for (int i=1;i<=sz;i++)
for (int j=0;j<26;j++)
ch[i][j]=0;
for (int i=1;i<=sz;i++) fa[i]=mx[i]=0;
sz=last=1;
int len=strlen(str);
for (int i=0;i<len;i++) extend(str[i]-'a');
pre();
int n,ans=0;
scanf("%d",&n);
while (n--)
{
scanf("%s",str);len=strlen(str);
int x=1;
for (int i=0;i<len;i++) x=ch[x][str[i]-'a'];
ans^=sg[x];
}
puts(ans?"Alice":"Bob");
}
return 0;
}