【男人八題】 A.String Game(字尾自動機 + sg函式)
阿新 • • 發佈:2018-12-16
題目大意:給一個模式串和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;
}