Date: 2026-06-06
Time: 16:46
This file solves LeetCode 28 — Find the Index of the First Occurrence in a String. It implements substring search: given haystack and needle, return the index where needle first appears, or -1 if absent.
It uses the KMP (Knuth-Morris-Pratt) algorithm rather than Python's built-in str.find() or str.index(), making the algorithmic intent explicit.
Solution.strStr(haystack, needle) -> intThe single method. Two phases:
Phase 1 — Build the LPS (Longest Proper Prefix which is also Suffix) array (lines 12–21):
lps[i] stores the length of the longest proper prefix of needle[0..i] that is also a suffix.i scans the pattern, length tracks the current matching prefix length.length > 0, fall back to lps[length - 1] — this is the key insight that avoids restarting from scratch.Phase 2 — Search haystack using the LPS table (lines 24–33):
i into haystack, j into needle.j == m, the full needle has been found — return i - j.j > 0, fall back j to lps[j - 1] instead of resetting to 0.j == 0, just advance i."aaaaab" in "aaaaaaaaab").Solution class with a method matching the problem's signature.Imports: None.
Imported by: The testsolution.py in the same directory, plus the "Imported By" list in the prompt shows hundreds of other test files — this is an artifact of the test harness importing all solutions uniformly, not a real dependency relationship. Only find-the-index-of-the-first-occurrence-in-a-string/testsolution.py exercises this code meaningfully.
strStr("hello", "ll")
1. Build LPS for "ll" → [0, 1]
2. Search:
i=0, j=0: 'h' vs 'l' → mismatch, j=0, advance i
i=1, j=0: 'e' vs 'l' → mismatch, j=0, advance i
i=2, j=0: 'l' vs 'l' → match, i=3, j=1
i=3, j=1: 'l' vs 'l' → match, i=4, j=2, j==m → return 4-2=2
0 <= lps[i] < i+1 for all i. Each entry is strictly less than the index + 1 (proper prefix, not the whole string).i or decreases j. Since j can only decrease a finite number of times before i must advance, the algorithm terminates in O(n + m) steps.j == m), guaranteeing the leftmost index.needle is empty (m = 0), the LPS loop doesn't execute, the search loop immediately hits j == m (both are 0), and returns 0 - 0 = 0. This matches LeetCode's expected behavior.None. The method always returns an int — either a valid index or -1. No exceptions are raised. Input validation is deferred to LeetCode's constraints (both inputs are guaranteed to be lowercase English strings with 1 <= needle.length <= haystack.length <= 10^4, though the code handles empty needle correctly anyway).
find-the-index-of-the-first-occurrence-in-a-string/test_solution.py — See what edge cases are tested (empty strings, full-string matches, no-match, single-char)find-the-index-of-the-first-occurrence-in-a-string/plan.md — Check whether KMP was a deliberate choice over alternatives like Rabin-Karp or Python builtinskmp-failure-function-fallback — The length = lps[length - 1] fallback on mismatch is the subtlest part of KMP; trace through a pattern like "AAACAAAA" to see cascading fallbacksfind-the-index-of-the-first-occurrence-in-a-string/solution.py:strStr — Test with adversarial input like haystack="aaaaaa", needle="aab" to see how KMP avoids O(n*m) backtrackingrepeated-substring-pattern/solution.py — Another problem where KMP's LPS array is often used (checking if a string is a repeated pattern)kmp-time-complexity — strStr runs in O(n + m) time where n = len(haystack) and m = len(needle), with no backtracking on haystackkmp-space-complexity — The only auxiliary allocation is the lps array of size m, making space complexity O(m)first-match-semantics — The search returns immediately on the first complete match, guaranteeing the leftmost occurrence indexempty-needle-returns-zero — When needle is empty, the method returns 0 without special-case code, as a consequence of the j == m check when both are 0