计蒜客 A String Game 后缀自动机+SG

题意

给你一个母串,然后再给你n个串,每次Alice或者Bob可以选择这n个串中的一个,在后面添加一个字符,使得这个串还是母串的子串,Alice先操作,不能操作者输

分析

后缀自动机裸上,每个点记一个SG值,然后异或起来就好了,裸题

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
inline int read()
{
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
char ss[N]; int step[N],fail[N],g[N][27],rt,tot=0,last;
void insert(int nx)
{
  int p,np,q,nq; p=last; np=++tot; step[np] = step[p]+1;
  while(p && !g[p][nx]){g[p][nx] = np; p=fail[p];}
  if(!p) fail[np] = 1;
  else
  {
    q = g[p][nx];
    if(step[q] == step[p]+1) fail[np]=q;
    else
    {
      nq = ++tot; step[nq] = step[p]+1;
      memcpy(g[nq],g[q],sizeof(g[nq]));
      fail[nq] = fail[q]; fail[q] = fail[np] = nq;
      while(p && g[p][nx] == q) g[p][nx] = nq,p=fail[p];
    }
  }last=np;
}
int R[N],rk[N]; int sg[N],cnt[27];
int main()
{
  while(scanf("%s",ss+1)!=EOF)
  {
    int len = strlen(ss+1);
    memset(step,0,sizeof(step));
    memset(fail,0,sizeof(fail));
    memset(g,0,sizeof(g));
    memset(sg,0,sizeof(sg));
    last=tot=1; for(int i=1;i<=len;i++) insert(ss[i]-'a'+1);
    for(int i=1;i<=tot;i++) R[i] = 0;
    for(int i=1;i<=tot;i++) R[step[i]] ++;
    for(int i=1;i<=tot;i++) R[i] += R[i-1];
    for(int i=tot;i>=1;i--) rk[R[step[i]]--] = i;
    for(int i=tot;i>=1;i--)
    {
      int x = rk[i]; for(int j=0;j<=26;j++) cnt[j] = 0;
      for(int j=1;j<=26;j++) if(g[x][j]) cnt[sg[g[x][j]]] ++;
      for(int j=0;j<=26;j++) if(!cnt[j]){sg[x] = j; break;}
    }
    int ans = 0;
    int n = read();
    for(int i=1;i<=n;i++)
    {
      scanf("%s",ss+1); len = strlen(ss+1);
      int u = 1; for(int j=1;j<=len;j++) u = g[u][ss[j]-'a'+1];
      ans ^= sg[u];
    }
    if(ans) printf("Alice\n");
    else printf("Bob\n");
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值