LeetCode Entry

459. Repeated Substring Pattern

21.08.2023 easy 2023 kotlin

careful to not shift by whole length

459. Repeated Substring Pattern easy blog post substack

image.png

Join me on Telegram

https://t.me/leetcode_daily_unstoppable/315

Intuition

Consider example, abc abc abc. Doing shift left 3 times we get the same string:

abcabcabc - original
bcabcabca - shift left by 1
cabcabcab - shift left by 1
abcabcabc - shift left by 1

Now, there is a technique called Rolling Hash: let’s calculate the hash like this: hash = x + 31 * hash. After full string hash calculated, we start doing shifts:

    // abcd
    // a
    // 32^0 * b + 32^1 * a
    // 32^0 * c + 32^1 * b + 32^2 * a
    // 32^0 * d + 32^1 * c + 32^2 * b + 32^3 * a
    // bcda
    // 32^0 * a + 32^1 * d + 32^2 * c + 32^3 * b = 32*(abcd-32^3a) +a=32abcd-(32^4-1)a

Observing this math equation, next rolling hash is shiftHash = 31 * shiftHash - 31^len + c

Approach

  • careful to not shift by whole length

Complexity

  • Time complexity: \(O(n)\), at most 2 full scans, and hashing gives O(1) time

  • Space complexity: \(O(1)\)

Code


    fun repeatedSubstringPattern(s: String): Boolean {
      var hash = 0L
      for (c in s) hash = c.toInt() + 31L * hash
      var pow = 1L
      repeat(s.length) { pow *= 31L }
      pow--
      var shiftHash = hash
      return (0 until s.lastIndex).any { i ->
        shiftHash = 31L * shiftHash - pow * s[i].toInt()
        shiftHash == hash &&
          s == s.substring(0, i + 1).let { it.repeat(s.length / it.length) }
      }
    }