Files
troupe-standup/wizard-work/beam/kmp.py
T

51 lines
1.4 KiB
Python

"""Knuth-Morris-Pratt substring search.
Provides:
- build_pi(pattern): prefix (failure) function, O(m)
- search(text, pattern): all start indices, O(n+m)
- search_count(text, pattern): number of (possibly overlapping) occurrences
Correctness notes:
* Empty pattern matches at every position (0..len(text)) per Python str.find semantics? We return [] for empty pattern to avoid O(n) noise; callers wanting positions can handle separately. Actually we return list(range(len(text)+1)) to match str.find('') behaviour? Tests pin: empty pattern -> [] (documented).
* Overlapping occurrences are reported.
"""
from typing import List
def build_pi(pattern: str) -> List[int]:
m = len(pattern)
pi = [0] * m
k = 0
for i in range(1, m):
while k > 0 and pattern[i] != pattern[k]:
k = pi[k - 1]
if pattern[i] == pattern[k]:
k += 1
pi[i] = k
return pi
def search(text: str, pattern: str) -> List[int]:
if not pattern:
return []
n, m = len(text), len(pattern)
if m > n:
return []
pi = build_pi(pattern)
res: List[int] = []
k = 0
for i in range(n):
while k > 0 and text[i] != pattern[k]:
k = pi[k - 1]
if text[i] == pattern[k]:
k += 1
if k == m:
res.append(i - m + 1)
k = pi[k - 1]
return res
def search_count(text: str, pattern: str) -> int:
return len(search(text, pattern))