bzoj4974 字串大師 KMP
阿新 • • 發佈:2018-11-12
明顯的,有$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 #definell 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; }