1. 程式人生 > >bzoj4974 字串大師 KMP

bzoj4974 字串大師 KMP

明顯的,有$next[i] = i - pre[i]$

根據$next[i]$構造比根據$pre[i]$簡單

如果$next[i] \neq 0$,那麼我們可以直接取前面的結果

否則,我們可以暴力的尋找$next[i - 1], next[next[i - 1]] ...$後一位中最小沒有出現過的字元

暴力的複雜度為$O(n)$

 

不妨考慮有一棵$next$樹

最壞情況下,我們會從每個葉子節點走到根一遍

對於需要走的每個葉子節點$x$,都有$next[x + 1] = 0$

並且從葉子節點走到根的複雜度為$O(next[x])$

由於有$next[i] \leq next[i - 1] + 1$,因此對於滿足$next[x + 1] = 0$的$next[x]$的和不會超過$n$

因此複雜度不超過$O(n)$

 

如果你閒的發慌,可以用可持久化線段樹做到$O(n \log \sum)$而不是$O(n \sum)$

其中$\sum$為字符集大小

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
namespace remoon {
    #define de double
    #define le long double
    #define ri register int
    #define
ll long long #define tpr template <typename ra> #define rep(iu, st, ed) for(ri iu = st; iu <= ed; iu ++) #define drep(iu, ed, st) for(ri iu = ed; iu >= st; iu --) #define gc getchar inline int read() { int p = 0, w = 1; char c = gc(); while(c > '
9' || c < '0') { if(c == '-') w = -1; c = gc(); } while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc(); return p * w; } } using namespace std; using namespace remoon; const int sid = 300050; int n; char s[sid]; int nxt[sid]; bool vis[40]; int main() { n = read(); rep(i, 1, n) { nxt[i] = i - read(); if(nxt[i]) s[i] = s[nxt[i]]; else { memset(vis, 0, sizeof(vis)); for(ri j = nxt[i]; j; j = nxt[j]) vis[s[j + 1] - 'a'] = 1; vis[s[1] - 'a'] = 1; for(ri j = 'a'; j <= 'z'; j ++) if(!vis[j - 'a']) { s[i] = j; break; } } printf("%c", s[i]); } return 0; }