Find the First Occurrence in a String
You get two strings, haystack and needle. Return the index in haystack where the first copy of needle starts, counting from 0. If needle never appears in haystack, return -1. Write the search yourself instead of calling a built-in substring search such as find or indexOf.
Function
- haystackstring
- the text to search in
- needlestring
- the string to look for
- Returnsinteger
- the index where the first copy of needle starts, or -1 if there is none
Constraints
1 ≤ haystack.length ≤ 5 × 1041 ≤ needle.length ≤ 5 × 104- Both strings contain only lowercase English letters.
needlemay be longer thanhaystack. Then it cannot appear, and the answer is-1.
Examples
- Input
- haystack = "bananarama"needle = "ana"
- Output
- 1
- Explanation
- The letters at indices 1 to 3 spell
ana. Another copy starts at index 3 and overlaps it, but the first copy counts, so the answer is 1.
- Input
- haystack = "pineapple"needle = "apples"
- Output
- -1
- Explanation
applestarts at index 4, but the haystack ends right after it, so the needle's finalshas nothing to match. The answer is-1.
- Input
- haystack = "abcabcabd"needle = "abcabd"
- Output
- 3
- Explanation
- The attempt at index 0 matches
abcab, then meets acwhere the needle wants ad. The copy at index 3 matches all six letters.
Follow-up
Can you return every index where needle starts, overlapping copies included, still in O(n + m) time?
Hints
Open them one at a time. Each one gives away a little more.
A copy of
needlecan only start at an index where it still fits insidehaystack. What is the last such index?When a long partial match fails, the brute force starts over one index later and rereads most of the same letters. The letters you already matched are a prefix of
needle, so you know them without looking at the haystack again.For every prefix of
needle, precompute the length of its longest proper prefix that is also its suffix. Scan the haystack once with a countkof matched letters; on a mismatch, shrinkkto that precomputed length instead of moving back in the haystack.
Solution
Checking every start position is correct, but a partial match that fails near its end is thrown away and the next start rereads the same letters. The Knuth-Morris-Pratt algorithm keeps that work: a table built from needle says how much of a failed match still counts, so the scan never moves back and runs in O(n + m).
Check every start position
Correct, but does not finish on the largest tests
Intuition
Call the lengths n and m. A copy can start at any index from 0 to n-m; a later start runs past the end. Try the starts from left to right, comparing letter by letter until the first difference. The first full match is the first copy, and a needle longer than the haystack leaves no start to try.
A haystack of 50,000 as and a needle of 24,999 as and then a b give 25,001 starts of 25,000 comparisons each: over 6 × 10^8 comparisons for an answer of -1.
Algorithm
- Let
nandmbe the lengths ofhaystackandneedle. - For each
startfrom 0 ton-m, setjto 0. - While
j < mandhaystack[start + j]equalsneedle[j], increasej. - If
jreachedm, every letter matched: returnstart. - If no start works, return
-1.
def strStr(haystack, needle):
n, m = len(haystack), len(needle)
for start in range(n - m + 1):
j = 0
while j < m and haystack[start + j] == needle[j]:
j += 1
if j == m:
return start
return -1Knuth-Morris-Pratt
Intuition
Searching abcabd in abcabcabd, the attempt at index 0 matches abcab and fails. Those letters end in ab, which is how the needle begins, so the next useful attempt already has two letters matched and you carry on from the same haystack position.
A border of a string is a shorter prefix that is also a suffix, like ab in abcab. lps[i] is the length of the longest border of needle[0..i]; for abcabd it is [0, 0, 0, 1, 2, 0]. After a mismatch with k letters matched, drop k to lps[k-1] and compare the same letter again. No copy is skipped: a copy that starts inside the failed attempt begins with a border of what was matched, and the longest border is tried first.
k rises at most once per haystack letter and every fallback lowers it, so the scan takes at most 2n steps.
Algorithm
- Build
lps: withk = 0, forifrom 1 tom-1, setk = lps[k-1]whilek > 0andneedle[i] != needle[k]; on a match increasek; storelps[i] = k. - Reset
kto 0 and walk the haystack with indexi. - While
k > 0andhaystack[i] != needle[k], setk = lps[k-1]. - If
haystack[i]equalsneedle[k], increasek. - If
kequalsm, returni-m+1. If the loop ends, return-1.
def strStr(haystack, needle):
m = len(needle)
# lps[i]: length of the longest proper prefix of needle[0..i] that is also its suffix
lps = [0] * m
k = 0
for i in range(1, m):
while k > 0 and needle[i] != needle[k]:
k = lps[k - 1]
if needle[i] == needle[k]:
k += 1
lps[i] = k
k = 0 # how many letters of needle are matched so far
for i, ch in enumerate(haystack):
while k > 0 and ch != needle[k]:
k = lps[k - 1] # fall back to the longest border, never move i back
if ch == needle[k]:
k += 1
if k == m:
return i - m + 1
return -1
Pitfalls and edge cases
- Letting the start run to
n-1instead ofn-m. When the haystack's end matches the needle's start, the comparison reads past the end. - Computing
n-mwith unsigned lengths (size_tin C++,usizein Rust) when the needle is longer: C++ wraps to a huge number and Rust panics in a debug build. Checkm > nfirst. - Writing the KMP fallback as an
ifinstead of awhile. Searchingaaainaabaa, thebneeds two fallbacks; stop after one and you report a false copy at index 2. - Rewinding the haystack index after a mismatch. Only
kchanges; movingiback brings backO(n · m). - Returning a 1-based index in Lua or R.
Frequently asked questions3
What is the time complexity of finding the first occurrence of a string?
Checking every start position takes O(n · m) time in the worst case and O(1) extra space, where n and m are the lengths of the haystack and the needle. Knuth-Morris-Pratt takes O(n + m) time and O(m) space for its table.
How do you build the KMP prefix table?
Run the matching loop on the needle against itself. Walk i from 1, keep k as the current border length, fall back with k = lps[k-1] on a mismatch, and store k in lps[i]. For aabaaab the table is [0, 1, 0, 1, 2, 2, 3].
Can you solve it with hashing instead of KMP?
Yes, with Rabin-Karp. Keep a rolling hash of each window of m haystack letters, updated in constant time as the window slides, and compare letters only when it equals the needle's hash. That runs in O(n + m) expected time; many collisions push it toward O(n · m).
Similar problems
Problems that use the same ideas. Solving two or three of them is what makes a pattern stick.
def strStr(haystack, needle):
# Write code hereTest #1
Test #2
Test #3
Arguments
haystack = "bananarama" needle = "ana"
Expected Output
1