How do we achieve substring-match under On time?
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Linear-time substring matching is possible when the algorithm avoids rechecking characters that it already knows how to reason about. The naive approach restarts too much work after mismatches, which can make it slow on repetitive input. Algorithms such as Knuth-Morris-Pratt and the Z algorithm achieve O(n + m) matching by preprocessing structure and reusing it during the scan.
Why the Naive Search Repeats Work
The naive method tries every alignment of the pattern against the text and compares characters until a mismatch appears. On repetitive inputs, many of those comparisons repeat work the algorithm effectively already learned.
For example, if a long prefix matched and then failed late, the naive method often restarts from the next text position and checks many of the same characters again.
To achieve linear time, the algorithm needs memory about partial matches so it can skip impossible alignments.
Use Knuth-Morris-Pratt
KMP preprocesses the pattern into an lps table. Each table entry stores the length of the longest proper prefix that is also a suffix for the pattern prefix ending at that position.
Build the lps table
Search the text with KMP
KMP is linear because the text index never moves backward. The pattern index is adjusted using the precomputed structure rather than restarting from zero every time.
The Z Algorithm Gives Another Linear Approach
The Z algorithm computes, for each position, the length of the longest prefix match starting there. For pattern searching, you combine the pattern, a separator, and the text, then compute Z values on the combined string.
Like KMP, the Z algorithm avoids repeated character comparisons by reusing information from earlier matches.
What Linear Time Really Means Here
For text length n and pattern length m, linear-time matching usually means O(n + m). The preprocessing of the pattern takes linear time in m, and the scan of the text takes linear time in n.
That is different from the naive worst case, which can degrade toward O(nm) on adversarial input.
This improvement matters most on large or repetitive text where repeated backtracking becomes expensive.
Edge Cases Still Matter
Even with a mathematically good algorithm, you still need to define behavior for:
- empty pattern
- pattern longer than the text
- overlapping matches
- byte-wise versus Unicode-normalized comparison
Those are not afterthoughts. They are part of the real contract of the search function.
Common Pitfalls
The biggest pitfall is claiming linear complexity while implementing a search that still restarts too much work after mismatches.
Another common issue is building the lps or Z table incorrectly. A small preprocessing bug can make the runtime look fast while silently missing matches.
People also forget to test repetitive or adversarial inputs, which are exactly the cases that separate linear algorithms from naive ones.
Summary
- Linear-time substring matching works by avoiding repeated comparisons after mismatches.
- KMP achieves this with the
lpsprefix-suffix table. - The Z algorithm provides another exact matching approach with the same linear-time guarantee.
- The target complexity is
O(n + m)for text lengthnand pattern lengthm. - Correct preprocessing and edge-case handling matter as much as the high-level algorithm choice.
Related reading
- How do we sort CloudWatch stream logs by 'most recent' in AWS console?
- How do you efficiently generate a list of K non-repeating integers between 0 and an upper bound N
- How do you get a directory listing sorted by creation date in python?
- How do you know where to perform rotations in an AVL tree?
- How do you de-normalise?
- How do you determine the ideal buffer size when using FileInputStream?
- How do you partition an array into 2 parts such that the two parts have equal average?
- How do you rotate a two dimensional array?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.