bzoj4641 基因改造 KMP / hash

依稀记得,$NOIP$之前的我是如此的弱小....

完全不会$KMP$的写法,只会暴力$hash$....

大体思路为把一个串的哈希值拆成$26$个字母的位权

即$hash(S) = sumlimits_{a} a * sum w^i * [s[i] == a]$

通过记录每个字母第一次出现的位置,用$26$的时间来确定$f$是什么

然后通过确定的$f$计算出$f$是正确的时候的$hash$值,和原串的$hash$值比较

复杂度$O(26n)$

自然取模....

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
namespace remoon {
    #define ri register int
    #define ll long long
    #define ull unsigned long long
    #define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
    #define drep(io, ed, st) for(ri io = ed; io >= st; io --)
}
using namespace std;
using namespace remoon;

#define sid 500050
char s[2005000], t[sid];

int n, m, tim, f[200];
int num[200], vis[200], nxt[200], tot;
ull val[200], wei[sid];
ull seed = 19260817;

inline void Init() {
    wei[0] = 1;
    rep(i, 1, m) wei[i] = wei[i - 1] * seed;
    rep(i, 1, m) {
        int le = t[i];
        if(!vis[le]) nxt[++ tot] = i, vis[le] = 1;
        val[le] += wei[m - i];
    }
    tim ++;
}

inline void Solve() {
    ull now = 0, tval = 0;
    rep(i, 1, m) now += s[i] * wei[m - i];
    rep(i, m, n) {
        
        tim ++;
        int flag = 0;

        rep(j, 1, tot) {
            int v = s[i - m + nxt[j]];
            if(vis[v] == tim) { flag = 1; break; }
            if(vis[v] != tim) vis[v] = tim;      
            f[j] = v;
        }
        
        if(!flag) {
            tval = 0;
            rep(j, 1, tot) 
            tval += f[j] * val[t[nxt[j]]];
            if(tval == now) write(i - m + 1);
        }
        
        now -= s[i - m + 1] * wei[m - 1];
        now *= seed; now += s[i + 1];
    }
}

int main() {
    scanf("%s", s + 1); n = strlen(s + 1); 
    scanf("%s", t + 1); m = strlen(t + 1);
    Init(); Solve();
    return 0;
}

现在我明白了$KMP$是非常伟大的算法....

对于此题而言,考虑每个字符的上一个字符离当前字符的距离,这可以成为一个新串

然后比对新串即可

特别的,如果上一个字符出现的位置超过了匹配长度,那么我们也要视作合法

但是,我们发现这种匹配满足有前效性,没有后效性,因此可以用$KMP$

复杂度$O(n)$,十分的优秀

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
namespace remoon {
    #define ri register int
    #define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
    #define drep(io, ed, st) for(ri io = ed; io >= st; io --)
}
using namespace std;
using namespace remoon;

const int sid = 1005000;

int n, m;
char s[sid], t[sid];
int lst[200], S[sid], T[sid], nxt[sid];

inline bool match(int x, int y) {
    if(y > m) return 0;
    if(x == T[y]) return 1;
    if(!T[y] && x >= y) return 1;
    return 0;
}

int main() {
    scanf("%s", s + 1); scanf("%s", t + 1);
    n = strlen(s + 1); m = strlen(t + 1);
    
    rep(i, 1, n) {
        S[i] = lst[s[i]] ? i - lst[s[i]] : 0;
        lst[s[i]] = i;
    }
    
    memset(lst, 0, sizeof(lst));
    rep(i, 1, m) {
        T[i] = lst[t[i]] ? i - lst[t[i]] : 0;
        lst[t[i]] = i;
    }
    
    for(ri i = 2, j = 0; i <= m; i ++) {
        while(j && !match(T[i], j + 1)) j = nxt[j];
        if(match(T[i], j + 1)) j ++; 
        nxt[i] = j;
    }
    
    for(ri i = 1, j = 0; i <= n; i ++) {
        while(j && !match(S[i], j + 1)) j = nxt[j];
        if(match(S[i], j + 1)) j ++;
        if(j == m) printf("%d
", i - m + 1);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/reverymoon/p/9949445.html