Блог пользователя larush

Автор larush, история, 13 месяцев назад, По-английски

Hello

So I was solving this problem (here), and a hashing-based solution seemed obvious to me. It is enough to look at the hash of the changed substring and get the final hash of the modified string using the prefix and suffix. I do not know much about hashing, just a little about polynomial (or rolling) hash. But it seems that my hash isn't safe enough. I tried a few things, but with no luck:

Spoiler

I used the standard primes M = 1e9+9 and p = 31 initially, as I learned from cp-algo. I have no clue what I'm doing wrong.

I hope someone could assist me in this and help me understand hashing a little better. Cheers!

off by one
  • Проголосовать: нравится
  • +10
  • Проголосовать: не нравится

»
13 месяцев назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

You can only improve probability of correct answer using hashing.

You can check SlavicG submission. He used $$$3$$$ different combination of $$$3$$$ $$$M$$$ (mod) values and $$$4$$$ different $$$p$$$ values to improve his probability of getting correct output.

You can also checkout his other submission during contest which failed to pass since they used only $$$1$$$ value of $$$M$$$ and $$$p$$$. submission-1 submission-2

UPD: You can checkout this blog for more information on multi-hashing and how to write hard to hack hashing code (and also how to hack poorly written hashes).

  • »
    »
    13 месяцев назад, # ^ |
      Проголосовать: нравится +4 Проголосовать: не нравится

    I do not think that the SlavicG submission you linked is safe from hacks. I've hacked many similar rolling hash solutions like that in that past. The reason it is hackable is that

    1. All of the bases used are constant (non-random).
    2. The prime mods used are small. The time complexity for a typical hash hack algorithm is O(sqrt(largest prime)).

    In my opinion, the easiest solution to be safe from hacks when using rolling hashes is to use a single large prime like $$$P=2^{61} - 1$$$, and then use a random base (pick it uniformly at random in [0, P)). But whichever option you choose to go with, do not make the base constant (non-random).

    Just a final note. SlavicG technically does randomize the bases using shuffle(all(plsnohack2), rng);, but there are only 24 different possible outcomes. So his choice of bases is effectively deterministic.

»
13 месяцев назад, # |
Rev. 3   Проголосовать: нравится +1 Проголосовать: не нравится

But it seems that my hash isn't safe enough.

Since all of your hashes are integers in range $$$[0, M)$$$, there are $$$M$$$ different possible hashes. Your code will work incorrectly if you encounter a hash collision, i.e. a case where two different strings hash to the same hash value.

In the problem you linked, you're comparing up to $$$2\cdot10^5$$$ hashes with each other. According to the birthday paradox you only need around $$$\sqrt{M}$$$ different hashes before it's expected that some two are the same. Since $$$2\cdot10^5$$$ is much larger than $$$\sqrt{M} \approx 31622$$$, it's very likely that you'll encounter a hash collision.

The easiest way to deal with this is to choose two different pairs of (multiplier, modulus) and calculate the hash with both; now the hash is the pair of these hashes. Now the number of hashes is on the order of $$$M^2$$$ and we'd expect a collision only once we have around $$$M$$$ strings, so two hashes should be good enough. Of course you can use more to be safe but it's probably not required.

»
13 месяцев назад, # |
Rev. 4   Проголосовать: нравится 0 Проголосовать: не нравится

using unsigned long long to hash, i will call it overflow hashing.

You can check my code for better understanding: https://codeforces.me/contest/1840/submission/208865643