Find the Index of the First Occurrence in a String
Easy · Arrays & Hashing · Explanation and code example
Rabin-Karp Algorithm (Rolling Hash)
How it works
Instead of comparing characters one by one, we can compare hash values of substrings. If we compute the hash of the needle and the hash of each window in the haystack, matching hashes suggest a potential match. The key insight is using a rolling hash: when sliding the window by one position, we can update the hash in O(1) time by removing the contribution of the outgoing character and adding the incoming one. Using two different hash functions reduces false positives.
Steps
- Compute the hash of the needle using two different hash functions (different bases and moduli).
- Compute the hash of the first window in the haystack (same length as the needle).
- Precompute the power values needed to remove the leftmost character's contribution.
- Slide the window through the haystack:
- If both hashes match the needle's hashes, return the current position.
- Update the rolling hash by removing the leftmost character and adding the new rightmost character.
- Handle negative values from the modulo operation.
- Return
-1if no match is found.
Complexity
- Time complexity:
O(n + m) - Space complexity:
O(1)
Where
nis the length of the stringheystackandmis the length of the stringneedle.
Things to watch for
Off-by-One Errors in Loop Bounds
When iterating through the haystack, the loop should run from 0 to n - m inclusive. A common mistake is using n - m - 1 or n - 1 as the upper bound. The former misses the case where the needle appears at the very end, while the latter causes out-of-bounds access when comparing characters.
Not Handling Empty Needle
When the needle is an empty string, the expected return value is 0 (the empty string is found at the beginning of any string). Forgetting to check for this edge case can lead to incorrect behavior or infinite loops in some implementations.
Python code example
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
if not needle:
return 0
base1, mod1 = 31, 768258391
base2, mod2 = 37, 685683731
n, m = len(haystack), len(needle)
if m > n:
return -1
power1, power2 = 1, 1
for _ in range(m):
power1 = (power1 * base1) % mod1
power2 = (power2 * base2) % mod2
needle_hash1, needle_hash2 = 0, 0
haystack_hash1, haystack_hash2 = 0, 0
for i in range(m):
needle_hash1 = (needle_hash1 * base1 + ord(needle[i])) % mod1
needle_hash2 = (needle_hash2 * base2 + ord(needle[i])) % mod2
haystack_hash1 = (haystack_hash1 * base1 + ord(haystack[i])) % mod1
haystack_hash2 = (haystack_hash2 * base2 + ord(haystack[i])) % mod2
for i in range(n - m + 1):
if haystack_hash1 == needle_hash1 and haystack_hash2 == needle_hash2:
return i
if i + m < n:
haystack_hash1 = (haystack_hash1 * base1 - ord(haystack[i]) * power1 + ord(haystack[i + m])) % mod1
haystack_hash2 = (haystack_hash2 * base2 - ord(haystack[i]) * power2 + ord(haystack[i + m])) % mod2
haystack_hash1 = (haystack_hash1 + mod1) % mod1
haystack_hash2 = (haystack_hash2 + mod2) % mod2
return -1
C++ code example
class Solution {
public:
int strStr(string haystack, string needle) {
if (needle.empty()) return 0;
long long base1 = 31, mod1 = 768258391;
long long base2 = 37, mod2 = 685683731;
int n = haystack.size(), m = needle.size();
if (m > n) return -1;
long long power1 = 1, power2 = 1;
for (int i = 0; i < m; i++) {
power1 = (power1 * base1) % mod1;
power2 = (power2 * base2) % mod2;
}
long long needleHash1 = 0, needleHash2 = 0;
long long haystackHash1 = 0, haystackHash2 = 0;
for (int i = 0; i < m; i++) {
needleHash1 = (needleHash1 * base1 + needle[i]) % mod1;
needleHash2 = (needleHash2 * base2 + needle[i]) % mod2;
haystackHash1 = (haystackHash1 * base1 + haystack[i]) % mod1;
haystackHash2 = (haystackHash2 * base2 + haystack[i]) % mod2;
}
for (int i = 0; i <= n - m; i++) {
if (haystackHash1 == needleHash1 && haystackHash2 == needleHash2) {
return i;
}
if (i + m < n) {
haystackHash1 = (haystackHash1 * base1 - haystack[i] * power1 + haystack[i + m]) % mod1;
haystackHash2 = (haystackHash2 * base2 - haystack[i] * power2 + haystack[i + m]) % mod2;
if (haystackHash1 < 0) haystackHash1 += mod1;
if (haystackHash2 < 0) haystackHash2 += mod2;
}
}
return -1;
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.