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; }
喵喵喵?喵喵喵! 喵喵喵......