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;
}

 

posted @ 2018-11-12 20:28  remoon  阅读(156)  评论(0编辑  收藏  举报