← Все вопросы

Как сравнить две произвольные подстроки за O(1) после препроцессинга хешей?

Задан 20 месяцев назад751 просмотров2 ответа
9

Понял идею полиномиального хеша, но не понимаю, как вычесть один хеш из другого, чтобы получить хеш подстроки s[l..r]. Там же степени мешают. Дайте, пожалуйста, рабочий код с префиксными хешами и степенями базы.

2 ответа

14
✓ Принятый ответ — помог автору

Считаем префиксные хеши H[i] = Σ_{k<i} s[k] * base^k mod M и заранее степени pw[i] = base^i. Тогда хеш подстроки s[l..r] (включительно) получается как (H[r+1] - H[l]) * base^{-l}, но чтобы не считать обратный элемент, обычно домножают на pw при сравнении.

struct Hashing {
    vector<uint64_t> H, pw;
    Hashing(const string& s, uint64_t base) {
        int n = s.size();
        H.assign(n + 1, 0); pw.assign(n + 1, 1);
        for (int i = 0; i < n; ++i) {
            H[i+1] = mulmod(H[i], base);
            H[i+1] = (H[i+1] + s[i]) % M;     // схема Горнера: старший разряд слева
            pw[i+1] = mulmod(pw[i], base);
        }
    }
    uint64_t get(int l, int r) {                  // хеш s[l..r]
        uint64_t res = (H[r+1] + M - mulmod(H[l], pw[r-l+1])) % M;
        return res;
    }
};

Здесь я взял схему Горнера (старший коэффициент — у первого символа), поэтому при «вырезании» префикса его домножают на pw[длина подстроки]. Сравнение get(l1,r1) == get(l2,r2)O(1), препроцессинг — O(n).

4

Частая ловушка: смешать две схемы (младший разряд слева vs Горнера). Они дают разные формулы вырезания — если в одном месте считаете H[i+1]=H[i]+s[i]*pw[i], то и get будет другим ((H[r+1]-H[l]) без домножения, но сравнивать можно только подстроки одинаковой длины либо нормировать на pw[l]). Выберите одну схему и не путайте. Я предпочитаю Горнера — она нагляднее.

Ваш ответ

Войдите, чтобы ответить на вопрос.