Fast algorithm for searching for substrings in a string
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The fastest substring-search strategy depends on what you mean by fast: fastest to implement, fastest in worst-case theory, or fastest in real-world text processing. For everyday application code, the best answer is often the language's built-in search. For algorithm study, the important names are Knuth-Morris-Pratt, Boyer-Moore, and Rabin-Karp.
Start With The Practical Answer
If you are writing ordinary application code, use the standard library search function unless you have a measured reason not to.
In Python:
The built-in implementation is highly optimized in native code and will usually beat a hand-written algorithm in real codebases.
That is the practical answer.
The Naive Algorithm
The naive approach checks the pattern at every possible starting position.
Its worst-case time complexity is O(n * m), where n is the text length and m is the pattern length.
It is simple, but it does repeated work when mismatches happen late.
Knuth-Morris-Pratt: Good Worst-Case Behavior
KMP avoids rechecking characters by preprocessing the pattern into a prefix table.
The full KMP search runs in O(n + m) worst-case time. That is algorithmically strong, especially when predictable worst-case performance matters.
Boyer-Moore: Often Fast In Practice
Boyer-Moore compares from the end of the pattern and can skip ahead aggressively when mismatches happen. On natural-language text and large alphabets, it is often very fast in practice.
The reason people like Boyer-Moore is not just asymptotic notation. It is that it often examines fewer characters than simpler algorithms on typical input.
That said, it is more complex to implement correctly than the naive method or KMP.
Rabin-Karp: Useful For Multiple Patterns Or Rolling Hashes
Rabin-Karp uses hashing. Instead of comparing the whole pattern at every position, it compares hash values and only performs full verification when hashes match.
A small rolling-hash sketch looks like this:
This sketch uses Python's hash in a simple way for illustration, not as a production rolling-hash implementation. The algorithm is interesting because it generalizes well to searching many patterns.
Which Algorithm Should You Choose?
A practical rule is:
- use the built-in string search for ordinary application code
- use KMP when you want strong worst-case guarantees and are implementing the algorithm yourself
- use Boyer-Moore when you care about very fast searching on typical text
- use Rabin-Karp when hash-based multi-pattern workflows make sense
The best algorithm is not universal. It depends on input characteristics and whether you care more about implementation simplicity, worst-case guarantees, or average-case speed.
Multiple Queries Change The Tradeoff
If you search the same text many times for many different patterns, suffix arrays, suffix automata, or suffix trees can become relevant. Those data structures shift the problem from "one search in one string" to "many searches after preprocessing."
That is a different problem class from a single substring search.
Common Pitfalls
The most common mistake is reimplementing a complex search algorithm by hand when the language runtime already provides an excellent optimized search.
Another mistake is quoting theoretical complexity without considering the actual workload. Boyer-Moore may outperform a theoretically elegant alternative on real text, while a built-in search may beat both for practical reasons.
Developers also confuse single-pattern and multi-pattern problems. The best tool can change completely when the query pattern changes many times.
Finally, do not treat all O(n + m) algorithms as interchangeable. Constants, alphabet size, and implementation quality matter in substring search.
Summary
- For most real programs, the built-in substring search is the best starting point.
- The naive algorithm is simple but can take
O(n * m)time in the worst case. - KMP gives
O(n + m)worst-case performance. - Boyer-Moore is often very fast on typical text.
- Rabin-Karp is useful when hashing and multi-pattern scenarios matter.

