Back to blog
Sep 18, 2025
24 min read

Find the Index of the First Occurrence in a String

Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Difficulty: Easy | Acceptance: 45.38% | Paid: No

Topics: Two Pointers, String, String Matching

Examples

Input

haystack = "sadbutsad", needle = "sad"

Output

0

Explanation

”sad” occurs at index 0 and 6. The first occurrence is at index 0, so we return 0.

Input

haystack = "leetcode", needle = "leeto"

Output

-1

Explanation

”leeto” did not occur in “leetcode”, so we return -1.

Constraints

- 1 <= haystack.length, needle.length <= 10^4
- haystack and needle consist of only lowercase English characters.

Brute Force / Naive String Matching

Intuition

A straightforward approach would be to check every possible position in the haystack to see if the needle occurs at that position. We can do this by iterating through the haystack and, at each position, comparing the substring with the needle.

Steps

  • Iterate through the haystack from index 0 to len(haystack) - len(needle). This ensures we don’t go out of bounds.
  • At each position i, compare the substring haystack[i:i+len(needle)] with the needle.
  • If a match is found, return the current index i. If the loop completes without finding a match, return -1.
python
def strStr(haystack: str, needle: str) -> int:
    if not needle:
        return 0
    
    needle_len = len(needle)
    haystack_len = len(haystack)
    
    for i in range(haystack_len - needle_len + 1):
        # Check if substring matches needle
        if haystack[i:i + needle_len] == needle:
            return i
    
    return -1

Complexity

  • Time: O(n * m) where n is the length of haystack and m is the length of needle. In the worst case, we check each position in the haystack and compare up to m characters.
  • Space: O(1) for most languages that support substring comparison efficiently. However, for languages like Java where substring creates a new string object, it could be O(m).
  • Notes: This approach is simple but can be inefficient for large inputs. The worst case occurs when there are many partial matches, forcing repeated comparisons.

KMP Algorithm (Knuth-Morris-Pratt)

Intuition

The key idea is to avoid unnecessary comparisons by pre-processing the needle to construct an auxiliary array (failure function or LPS array) that provides information about how the pattern matches against shifts of itself. This allows us to skip ahead when a mismatch occurs, rather than starting the comparison from scratch.

Steps

  • Pre-process the needle to compute the LPS (Longest Proper Prefix which is also Suffix) array. This array stores the length of the longest proper prefix which is also a suffix for each sub-pattern of the needle.
  • Use two pointers, one for the haystack and one for the needle. Iterate through the haystack.
  • If characters match, advance both pointers. If they don’t match, use the LPS array to determine where to continue matching from in the needle without moving back in the haystack.
  • If we reach the end of the needle, we’ve found a match. Return the starting position of this match in the haystack.
  • If we finish processing the haystack without finding a full match, return -1.
python
def strStr(haystack: str, needle: str) -> int:
    if not needle:
        return 0
    
    def compute_lps(pattern):
        m = len(pattern)
        lps = [0] * m
        length = 0
        i = 1
        
        while i &lt; m:
            if pattern[i] == pattern[length]:
                length += 1
                lps[i] = length
                i += 1
            else:
                if length != 0:
                    length = lps[length - 1]
                else:
                    lps[i] = 0
                    i += 1
        return lps
    
    n, m = len(haystack), len(needle)
    lps = compute_lps(needle)
    
    i = j = 0
    while i &lt; n:
        if needle[j] == haystack[i]:
            i += 1
            j += 1
        
        if j == m:
            return i - j
        elif i &lt; n and needle[j] != haystack[i]:
            if j != 0:
                j = lps[j - 1]
            else:
                i += 1
    
    return -1

Complexity

  • Time: O(n + m) where n is the length of haystack and m is the length of needle. Both the preprocessing of the needle (LPS array computation) and the main matching process take linear time.
  • Space: O(m) for storing the LPS array of size m.
  • Notes: This is the optimal algorithm for this problem. It avoids backtracking in the haystack by using information from previous matches. It’s particularly efficient when the needle has repetitive patterns.

Rabin-Karp Algorithm

Intuition

Instead of comparing characters directly, we can use hashing to compare substrings. Compute a hash of the needle and then compute rolling hashes of substrings of the haystack with the same length as the needle. When a hash matches, we do a character-by-character verification to avoid false positives due to hash collisions.

Steps

  • Choose a suitable base and a large prime number for the hash function. Calculate the hash of the needle.
  • Calculate the hash of the first window (substring of haystack with length equal to needle) in the haystack.
  • Use a rolling hash technique to compute hashes of subsequent windows in constant time.
  • When a hash value matches that of the needle, perform a character-by-character comparison to confirm the match (to handle hash collisions).
  • If a confirmed match is found, return the starting index of the window. If no match is found after checking all windows, return -1.
python
def strStr(haystack: str, needle: str) -> int:
    if not needle:
        return 0
    
    base = 256
    prime = 101
    
    n, m = len(haystack), len(needle)
    if m &gt; n:
        return -1
    
    # Calculate hash of needle and first window of haystack
    needle_hash = 0
    window_hash = 0
    h = 1
    
    # Calculate h = pow(base, m-1) % prime
    for i in range(m - 1):
        h = (h * base) % prime
    
    for i in range(m):
        needle_hash = (base * needle_hash + ord(needle[i])) % prime
        window_hash = (base * window_hash + ord(haystack[i])) % prime
    
    # Slide the window over haystack
    for i in range(n - m + 1):
        if needle_hash == window_hash:
            # Check characters one by one
            if haystack[i:i + m] == needle:
                return i
        
        # Calculate hash for next window
        if i &lt; n - m:
            window_hash = (base * (window_hash - ord(haystack[i]) * h) + ord(haystack[i + m])) % prime
            if window_hash &lt; 0:
                window_hash += prime
    
    return -1

Complexity

  • Time: Average and best case: O(n + m) where n is the length of haystack and m is the length of needle. Worst case: O(n * m) when there are many spurious hits requiring character-by-character verification.
  • Space: O(1) as we only use a constant amount of extra space.
  • Notes: The Rabin-Karp algorithm is particularly useful when searching for multiple patterns or when the alphabet size is large. The performance depends on the choice of base and prime numbers for the hash function, and the likelihood of hash collisions.