LeetCode #28 Easy

Find the Index of the First Occurrence in a String

Find the Index of the First Occurrence in a String is LeetCode 28 (Easy). It used to be called Implement strStr(), after the C library function that does the same job.

  • Given two strings haystack and needle, return the index where needle first appears inside haystack.
  • Return -1 if it does not appear at all.

Both strings hold only lowercase letters and are at most 10⁴ characters long.

Constraints
  • 1 <= haystack.length, needle.length <= 10⁴
  • haystack and needle consist of only lowercase English characters.
stringtwo-pointersstring-matching
Open on LeetCode ↗
02

Intuition

A match can begin only at a start s where the whole needle still fits, so s runs from 0 to n - m (where n and m are the two lengths). Try each start from left to right, and compare the needle with the haystack one character at a time.

The first mismatch rules that start out, so the scan slides on to the next one. A start where all m characters agree is the answer, and because starts are tried in increasing order, it is automatically the first occurrence. If every start fails, the needle is not there.

How to spot this pattern

Finding where one string first appears inside another is the base problem of string matching. This start-by-start comparison, often called naive string matching, is the baseline that faster methods such as KMP, Rabin-Karp and the Z-function improve on, so it is worth being able to write it without bugs.

03

Approach

Try it first

Before reading on, take haystack "mississippi" and needle "issip". At which starts does the comparison get past the first character, and which start finally matches? Aim for O((n - m + 1) · m) time and O(1) extra space.

1

List the starts where the needle fits

A match starting at s uses positions s to s + m - 1, so s can be at most n - m. Looping over range(n - m + 1) tries exactly those starts, and the loop runs zero times when the needle is longer than the haystack.

2

Compare one start character by character

Set j = 0 and increase it while haystack[s + j] == needle[j]. Stop at the first mismatch: one wrong character is enough to rule out this start, so the rest of the needle does not need to be read.

3

Return the start on a full match

If j reached m, every needle character matched, so return s. Starts are tried left to right, so the first full match is the first occurrence, and no later start needs checking.

4

Return -1 only after the last start

A mismatch at one start says nothing about the next one, so the loop just moves on. Only when start n - m has also failed is it certain the needle never appears, and the function returns -1.

04

Find the Index of the First Occurrence in a String solution in Python | C++ | Java

▶1class Solution:
▶2 def strStr(self, haystack, needle):
▶3 n, m = len(haystack), len(needle)
▶4 for start in range(n - m + 1):
▶5 j = 0
▶6 while j < m and haystack[start + j] == needle[j]:
▶7 j += 1
▶8 if j == m:
▶9 return start
▶10 return -1
haystacks0a1d2b3u4t5s6a7d8sads = 0needlestarts 0 … 6 can fit the needle
n, m9, 3
starts0 to 67 to try
The needle has 3 characters, so a match starting at s ends at s + 2. That index must be at most 8, so the last useful start is 6. Start at 0 with nothing compared yet.
haystacks0a1d2b3u4t5s6a7d8s=ads = 0needle's' = 's' → next char
s0
j0haystack[0] vs needle[0]
The first characters agree, so this start is still alive. Compare the next pair.
haystacks0a1d2b3u4t5s6a7d8s=a=ds = 0needle'a' = 'a' → next char
s0
j1haystack[1] vs needle[1]
Character 1 agrees as well, so this start is still alive. Compare the next pair.
haystacks0a1d2b3u4t5s6a7d8s=a=d=s = 0needle'd' = 'd' → all 3 match
s0
j2haystack[2] vs needle[2]
The last needle character matches too, so all 3 characters agree at start 0.
haystacks0a1d2b3u4t5s6a7d8s=a=d=s = 0needlefull match → return 0
j3equals m
return0
j reached 3, so the needle sits at index 0. Every start before it already failed, so this is the first occurrence and the scan stops. Later starts are never examined.
05

Common pitfalls

Trying every index of the haystack as a start

✗ Wrong
for start in range(n):
✓ Right
for start in range(n - m + 1):

Starts past n - m leave too few characters for the needle. With haystack "abc" and needle "cd", start 2 matches c and then reads haystack[3]: an IndexError in Python and an out-of-bounds read in C++.

Returning -1 at the first mismatch

✗ Wrong
if haystack[start + j] != needle[j]:
    return -1
✓ Right
if haystack[start + j] != needle[j]:
    break  # try the next start

A failed start only rules out that one start. With haystack "aab" and needle "ab", start 0 fails on the second character, so this returns -1, but the needle sits at index 1.

Checking for a full match with m - 1

✗ Wrong
if j == m - 1:
    return start
✓ Right
if j == m:
    return start

After the loop, j is the number of characters that matched. With haystack "abc" and needle "abd", j stops at 2, which equals m - 1, so the wrong check reports a match at 0 even though c is not d.

06

Edge cases

Needle longer than the haystack, e.g. "aaa" and "aaaa"

n - m + 1 is 0 or less, so the loop never runs and the function returns -1 without reading a character.

Needle equal to the haystack

Only start 0 fits. All characters match, so the answer is 0.

Match at the last possible start, e.g. "abcd" and "cd"

Start 2 equals n - m, which the loop includes, so the match is found and 2 is returned.

07

Complexity

Time
O((n − m + 1) · m)
Space
O(1)
There are n − m + 1 starts and each compares at most m characters. Most starts fail within a character or two, so typical inputs run close to O(n). Only the two lengths, s and j are stored.
08

Find the Index of the First Occurrence in a String FAQ

Is this brute force fast enough, or is KMP required?

It is fast enough here. The worst input is 10,000 as with a needle of 4,999 as and a final b: about 5,000 starts times 5,000 comparisons, or 2.5 × 10⁷ steps, which runs in roughly a second even in plain Python. KMP gets O(n + m) by never moving backwards in the haystack; reach for it when an interviewer asks for linear time.

Can I just call the built-in find?

haystack.find(needle) in Python, indexOf in Java and string::find in C++ all give the same answer (C++ returns npos instead of -1). They are accepted, but interviewers ask this problem to see the loop written by hand, so mention the built-in and then write the scan.