1. 程式人生 > >【男人八題】 A.String Game(字尾自動機 + sg函式)

【男人八題】 A.String Game(字尾自動機 + sg函式)

題目大意:給一個模式串和n個它的子串,Alice和Bob玩遊戲,Alice先手,每回合任選一個子串,該回合輪到的人在它後面加一個字母,並且保證加了之後的新串仍然是模式串的子串。輪到後沒辦法保證上述新增要求的人輸。

(雖然題目沒有說,但是字符集是小寫字母)

可以看出這其實是一個經典的n堆nim博弈,可以用每個子串的sg函式異或值來求。

問題就在於求sg函式需要列舉所有走法,然後再取不與走了之後狀態sg值重複的最小非負。

Sam很好的提供了所有走法,只要能在自動機裡走,就代表生成的新串仍然是模式串的子串。

並且因為他們採取最優走法,所以sam的終點(last)一定是每個給定子串最終的歸宿(最終遊戲局面)。

即sg[last] = 0。

那麼就可以將自動機排序後從後往前求每個節點的sg,然後把給定子串走到對應狀態,取出其sg值,全部異或後即是要求的最終sg。

這樣做其實把這個遊戲轉換成了取石子,加字元實際上是從剩下可走的字符集(石子堆)中取字元。

jojo!我不做女裝大佬啦,我在男人八題簽到啦!!!!!!!

感謝sg-master權哥幫我複習sg函式。

ac程式碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 100005;
int n;
char s[maxn];
char t[105][maxn];

struct
Sam { int next[maxn << 1][26]; int link[maxn << 1], step[maxn << 1]; int sg[maxn << 1], vis[30]; int a[maxn], b[maxn << 1]; int sz, last, len, root; void init() { for(int i = 0; i <= sz; i++) { memset(next[i], 0, sizeof(next[i])); } memset(a, 0, sizeof(a)
); root = sz = last = 1; } void add(int c) { int p = last; int np = ++sz; last = np; step[np] = step[p] + 1; while(!next[p][c] && p) { next[p][c] = np; p = link[p]; } if(p == 0) { link[np] = root; } else { int q = next[p][c]; if(step[p] + 1 == step[q]) { link[np] = q; } else { int nq = ++sz; memcpy(next[nq], next[q], sizeof(next[q])); step[nq] = step[p] + 1; link[nq] = link[q]; link[q] = link[np] = nq; while(next[p][c] == q && p) { next[p][c] = nq; p = link[p]; } } } } void build() { init(); for(int i = 0; s[i] ; i++) { add(s[i] - 'a'); } for(int i = 1; i <= sz; i++) { a[step[i]]++; } for(int i = 1; i <= step[last]; i++) { a[i] += a[i - 1]; } for(int i = 1; i <= sz; i++) { b[a[step[i]]--] = i; } } void grundy() { sg[last] = 0; for(int i = sz; i > 1; i--) { int e = b[i]; int g = 0; memset(vis, 0, sizeof(vis)); for(int j = 0; j < 26; j++) { if(!next[e][j]) { continue; } vis[sg[next[e][j]]] = 1; } while(vis[g]) { ++g; } sg[e] = g; } } void solve() { build(); grundy(); int sum = 0; for(int i = 1; i <= n; i++) { int p = root; for(int j = 0; t[i][j] ; j++) { p = next[p][t[i][j] - 'a']; } sum ^= sg[p]; } printf("%s\n", sum ? "Alice" : "Bob"); } } sam; int main() { while(~scanf("%s", s)) { scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%s", t[i]); } sam.solve(); } return 0; }