是男人就过 8 题--Pony - A - 后缀自动机+SG函数

题目链接: 点击打开链接


解题思路:在后缀自动机的有向图上建立SG函数,然后去求每个子串在树上的节点号,异或个各个节点号sg值就行了。


#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
int n,m,a[mx],tot,last,ma[mx],mi[mx];
int fa[mx],id[mx][28],len[mx];
int sg[mx];
char str[mx],Ts[mx];
void init()
{
	last = tot = 1;
	fa[1] = len[1] = 0;
	memset(id[1],0,sizeof(id[1]));
	memset(sg,-1,sizeof(sg));
	//memset(v,0,sizeof(v));
}
void suf_auto(int ch)
{
	int np = ++tot,p = last;
	memset(id[np],0,sizeof(id[np]));
	len[np] = len[last] + 1,last = np;
	while(p&&!id[p][ch]) id[p][ch] = np,p = fa[p];
	if(!p) fa[np] = 1;
	else{
		int q = id[p][ch];
		if(len[p]+1==len[q]) fa[np] = q;
		else{
			int nq = ++tot;
			copy(id[q],id[q]+sizeof(id[q]),id[nq]);
			fa[nq] = fa[q];
			len[nq] = len[p] + 1;
			fa[q] = fa[np] = nq;
			while(p&&id[p][ch]==q) id[p][ch] = nq,p = fa[p];
		}
	} 
}
void get_sg(int x)
{
	bool vis[30];
	for(int i=0;i<30;i++) vis[i] = 0;
	for(int i=0;i<26;i++){
		if(!id[x][i]) continue;
		if(sg[id[x][i]]==-1) get_sg(id[x][i]);
		vis[sg[id[x][i]]] = 1;
	}
	for(int i=0;i<27;i++) if(!vis[i])
	{
		sg[x] = i;
		break;
	}
}
int get_node(char *p)
{
	int lon = strlen(p);
	int root = 1;
	for(int i=0;i<lon;i++){
		root = id[root][p[i]-'a'];
	}
	return root;
}
int main()
{	
	while(~scanf("%s",str)){
		init();
		int lon = strlen(str);
		for(int i=0;i<lon;i++) suf_auto(str[i]-'a');
		get_sg(1);
		scanf("%d",&m);
		int ans = 0;
		while(m--){
			scanf("%s",Ts);
			int k = get_node(Ts); 
			ans ^= sg[k];
		}
		puts(ans?"Alice":"Bob");
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值