Построение хеш-функций для строк

Гоша: Как вычислять хеш-функцию для целых чисел — ясно. А как вычислять хеш-функцию для строк?
Тимофей: Существует метод, который называется полиномиальный хеш, где хеш строки sss длины nnn вычисляются по формуле:
h2(s)=(s0an−1+s1an−2+⋯+sn−2a+sn−1) mod m h_{2}(s)=\left(s_{0} a^{n-1}+s_{1} a^{n-2}+\cdots+s_{n-2} a+s_{n-1}\right) \bmod m h2​(s)=(s0​an−1+s1​an−2+⋯+sn−2​a+sn−1​)modm.
Где s0,s1,...,sn−1s_0, s_1, ..., s_{n-1}s0​,s1​,...,sn−1​ — коды символов (например, ASCII-коды), а mmm и aaa — выбранные константы.
Тимофей: Нужно aaa подбирать таким образом, чтобы при изменении одного символа значение функции сильно менялось. С этим связано одно из свойств, которыми должна обладать хорошая хеш-функция. Про эти свойства я расскажу чуть позже.
Хотим, чтобы все значения s⋅a mod m,0⩽s<ms \cdot a \bmod m, 0 \leqslant s < ms⋅amodm,0⩽s<m были различными. На самом деле для этого достаточно, чтобы aaa и mmm были взаимно простыми.
Обычно в качестве aaa берут какое-то простое число.
Для избежания коллизий, если строки короткие, лучше выбирать aaa побольше. Иначе значения будут скапливаться в начале массива. Если строки длинные, выбор величины aaa не так важен. В этом случае в сумму будут входить значения aaa в достаточно больших степенях.
Алла: А можешь привести пример?
Тимофей: Вычислим хеш от строки “ABACB”. ASCII-коды символов “A”, “B” и “C” равны 65, 66 и 67.
В качестве констант возьмём: a=3a=3a=3, а m=97m=97m=97.
Тогда значение хеш-функции вычисляется так:
(65⋅34+66⋅33+65⋅32+67⋅31+66⋅30) mod 97=42.(65 \cdot 3^4 + 66 \cdot 3^3 + 65 \cdot 3^2 + 67 \cdot 3^1 + 66 \cdot 3^0) \bmod 97 = 42.(65⋅34+66⋅33+65⋅32+67⋅31+66⋅30)mod97=42.
Полиномиальный хеш можно вычислить эффективно с использованием метода Горнера:
h2(s)=((((s0a+s1)a+s2)a+⋯+sn−2)a+sn−1) mod m.h_{2}(s)=\left(\left(\left(\left(s_{0} a+s_{1}\right) a+s_{2}\right) a+\cdots+s_{n-2}\right) a+s_{n-1}\right) \bmod m.h2​(s)=((((s0​a+s1​)a+s2​)a+⋯+sn−2​)a+sn−1​)modm.
Может воспользуемся методом Горнера для нашего примера:
((((65⋅3+66)⋅3+65)⋅3+67)⋅3+66) mod 97=42.((((65 \cdot 3+66)\cdot 3 + 65) \cdot 3 + 67) \cdot 3+ 66) \bmod 97 = 42.((((65⋅3+66)⋅3+65)⋅3+67)⋅3+66)mod97=42.
Тимофей: При использовании полиномиального хеширования значение хеш-функции любой строки можно вычислить за константное время. Для этого нужно сделать предобработку, которая занимает O(n)O(n)O(n). Про этот метод вы узнаете немного позже, когда я буду рассказывать про специальные алгоритмы работы со строками.
Чему будет равен полиномиальный хеш от строки Kondratiy при использовании констант a=5a = 5a=5 и m=113m = 113m=113?
Алла: А расскажешь про свойства, которыми должна обладать хорошая хеш-функция?
Тимофей: Да, конечно. Скоро всё узнаете.
Решите задачи C, I, L: https://contest.yandex.ru/contest/19095/problems/