String Matching: All Occurrences — Problem & Solution
String Matching: All Occurrences solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A hard problem.
String Matching: All Occurrences is a hard coding problem on strings, rolling hash and string matching.
- Difficulty: Hard
- Topics: Strings, Rolling Hash, String Matching
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
Given text and a non-empty pattern, return all start indices where pattern occurs in text (overlaps count), in increasing order.
Aim for average O(n + m) time — the Rabin–Karp rolling hash achieves it.
Example 1
Input: text = "abababa", pattern = "aba"
Output: [0,2,4]
Explanation: Occurrences may overlap.
Example 2
Input: text = "aaaa", pattern = "b"
Output: []
Constraints
1 <= text.length <= 401 <= pattern.length <= 8Lowercase English letters.
Hints for String Matching: All Occurrences
Hint 1
A rolling hash lets you update the window hash in O(1) as it slides.
Hint 2
On hash match, verify characters to rule out collisions.
How to solve String Matching: All Occurrences
Report every start where the pattern matches, including overlapping ones — so after a hit you continue from the very next index rather than skipping past the match. The reference below compares directly at each start, bailing at the first mismatch; Rabin–Karp reaches average linear time by comparing a rolling hash first and only verifying characters when the hashes agree.
Approach
- Let
nandmbe the two lengths; ifm > nthere are no occurrences. - For each start
ifrom0ton - m, compare the pattern againsttextfromi. - Record
ion a full match. - Continue from
i + 1regardless — overlaps count. - Return the collected indices, already in increasing order.
Why it works
Advancing by one after a hit is what makes overlapping occurrences appear: in "aaaa" with pattern "aa", jumping past a match would report only indices 0 and 2 and miss index 1. Scanning starts left to right gives the increasing order for free. The rolling hash speeds the filtering step — updating the window's hash in constant time as it slides — but the verification on a hash match is still needed, because distinct strings can share a hash.
Complexity
- Time —
O(n · m) worst case, O(n + m) average with a rolling hash - Space —
O(1) beyond the output
Pitfalls
- Overlaps count, so never skip ahead by the pattern length after a match.
- Bound the outer loop at
n - mso the comparison stays in range. - No occurrences means an empty list, not
[-1]. - With Rabin–Karp, always verify on a hash match — skipping that step makes the answer wrong on a collision.
Reference solution
Python
from typing import List
def findOccurrences(text: str, pattern: str) -> List[int]:
n, m = len(text), len(pattern)
if m > n:
return []
BASE = 131
MOD = 1000000007
target = 0
cur = 0
power = 1
for i in range(m):
target = (target * BASE + ord(pattern[i])) % MOD
cur = (cur * BASE + ord(text[i])) % MOD
if i > 0:
power = (power * BASE) % MOD
out = []
for i in range(n - m + 1):
if cur == target and text[i:i + m] == pattern:
out.append(i)
if i + m < n:
cur = ((cur - ord(text[i]) * power) * BASE + ord(text[i + m])) % MOD
cur %= MOD
return outJava
public static int[] findOccurrences(String text, String pattern) {
List<Integer> out = new ArrayList<>();
int n = text.length(), m = pattern.length();
for (int i = 0; i + m <= n; i++) {
int j = 0;
while (j < m && text.charAt(i + j) == pattern.charAt(j)) j++;
if (j == m) out.add(i);
}
int[] res = new int[out.size()];
for (int i = 0; i < out.size(); i++) res[i] = out.get(i);
return res;
}C++
vector<int> findOccurrences(string text, string pattern) {
vector<int> out;
int n = (int) text.size(), m = (int) pattern.size();
for (int i = 0; i + m <= n; i++) {
int j = 0;
while (j < m && text[i + j] == pattern[j]) j++;
if (j == m) out.push_back(i);
}
return out;
}JavaScript
var findOccurrences = function(text, pattern) {
const n = text.length, m = pattern.length;
const out = [];
if (m > n) return out;
for (let i = 0; i + m <= n; i++) {
let ok = true;
for (let j = 0; j < m; j++) {
if (text[i + j] !== pattern[j]) { ok = false; break; }
}
if (ok) out.push(i);
}
return out;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 515 strings problems · the whole catalogue
Learn the technique: Strings