Browse curriculum

Find the Index of the First Occurrence in a String

Return the index where needle first appears in haystack, or -1, by lining needle up at each start and comparing character by character with two pointers.

Problem Understanding

Find the Index of the First Occurrence in a String (LeetCode 28, the classic strStr): given haystack and needle, return the index of the first place needle appears in haystack, or -1 if it never does.

Examples: haystack = "sadbutsad", needle = "sad" → 0; haystack = "leetcode", needle = "leeto" → -1.

The Intuition: Two Pointers, One Window

Put needle under haystack and slide it right one step at a time:

  1. Pointer i is where needle starts in haystack.
  2. Pointer j walks needle while the characters agree.
  3. j reaches m: every character matched, so i is the answer.
  4. A mismatch: this start fails; reset j and try i + 1.

The same compare-and-advance step drives Is Subsequence, and Repeated String Match runs this search on repeated copies of a string.

Interactive Walkthrough

needle SITS UNDER ITS START

READY

haystack

m
i
s
s
i
s
s
i
p
p
i

needle

i
s
s
i
p

i

:

j

0

answer

:

 

needle has length 5, so it can start at 0 to 6 (7 starts)

Next

Line needle up at i = 0, j = 0

haystack

needle

Legend & Complexity

The window needle covers at this start

Characters matched so far (j of them)

The character that breaks the match

Time

O(n · m)

Space

O(1)

Watch needle slide along haystack: green cells have matched, the red cell is where a start fails. Try Near misses to see the slow case, where each start gets far before failing.

Dry Run Table

Input: haystack = "mississippi", needle = "issip" (n = 11, m = 5, starts 0 to 6).

i Characters matched Stops at
0 0 "m" ≠ "i"
1 4 ("issi") "s" ≠ "p"
2 0 "s" ≠ "i"
3 0 "s" ≠ "i"
4 5 ("issip") full match

Answer: 4.

The Solution Template

Two nested pointers: the outer loop picks the start, the inner loop compares.

Find the Index of the First Occurrence in a String Code
1var strStr = function(haystack, needle) {
2 const n = haystack.length, m = needle.length;
3 for (let i = 0; i + m <= n; i++) {
4 let j = 0;
5 while (j < m && haystack[i + j] === needle[j]) j++;
6 if (j === m) return i;
7 }
8 return -1;
9};

Edge Cases & Common Mistakes

  • needle longer than haystack: no start exists; the loop body never runs and the answer is -1.
  • Off-by-one on the bound: the last start is n - m, so the loop is i + m <= n (or range(n - m + 1)).
  • Forgetting to reset j: each start compares from the beginning of needle.
  • Returning the last match: stop at the first full match; later ones do not matter.
  • Match at the very end: haystack = "hello", needle = "lo" → 3, the last valid start.

Summary

Try each start i from 0 to n - m and compare needle character by character; return the first i where all m characters match, else -1. O(n · m) time and O(1) space; KMP brings it to O(n + m).

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Bound the starts

    needle can start at i = 0 to n - m; anything later runs off the end.

  2. Compare

    At each start, advance j while haystack[i + j] equals needle[j].

  3. Decide

    If j reaches m, return i; otherwise move to the next start. After the last start, return -1.

Frequently asked questions

Why does i only go up to n - m?

A match starting at i needs m characters, so it ends at i + m - 1. Past n - m that index falls off the end of haystack, so those starts can never match and are skipped. If needle is longer than haystack there are no starts at all and the answer is -1.


What is the worst case of this two-pointer method?

Inputs like haystack = "aaaaaaab" and needle = "aaab": almost every start matches most of needle before failing on the last character, so the work is about (n - m + 1) · m comparisons, O(n · m). Typical text fails within a character or two, so it is fast in practice.


How do you solve it in O(n + m)?

KMP precomputes, for each prefix of needle, the longest proper prefix that is also a suffix. On a mismatch it shifts needle by that much instead of restarting at the next index, so the haystack pointer never moves backwards. Rabin-Karp gets the same expected time with a rolling hash of each window.


Can I just call indexOf or find?

In real code, yes: haystack.indexOf(needle) in JavaScript, haystack.find(needle) in Python. In an interview, say so, then write the loop, because the question is checking that you can handle the window bounds and the restart correctly.

New to the technique behind this problem? Watch it run step by step:

Two Pointers Visualization

Applying for software engineering roles? Free, no sign-up:

AI resume checker: match your resume to the job description