1. 程式人生 > >bzoj4641 基因改造 KMP / hash

bzoj4641 基因改造 KMP / hash

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

完全不會$KMP$的寫法,只會暴力$hash$....

 

大體思路為把一個串的雜湊值拆成$26$個字母的位權

即$hash(S) = \sum\limits_{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\n", i - m + 1);
    }
    return 0;
}