String Pattern Matching Naive in DSA C++ - Time & Space Complexity
We want to understand how long it takes to find a pattern inside a text using a simple method.
How does the time needed grow when the text or pattern gets bigger?
Analyze the time complexity of the following code snippet.
bool naivePatternMatch(const std::string &text, const std::string &pattern) {
int n = text.size();
int m = pattern.size();
for (int i = 0; i <= n - m; i++) {
int j = 0;
while (j < m && text[i + j] == pattern[j]) {
j++;
}
if (j == m) return true;
}
return false;
}
This code checks every possible place in the text to see if the pattern matches starting there.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The outer loop tries each possible start position in the text.
- How many times: Up to n - m + 1 times, where n is text length and m is pattern length.
- Secondary operation: The inner while loop compares characters one by one.
- How many times: Up to m times for each start position.
- Dominant operation: The character comparisons inside the inner loop.
For each position in the text, we may compare up to all characters in the pattern.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 (text), 3 (pattern) | About 8 * 3 = 24 comparisons |
| 100 (text), 5 (pattern) | About 96 * 5 = 480 comparisons |
| 1000 (text), 10 (pattern) | About 991 * 10 = 9,910 comparisons |
Pattern observation: The total work grows roughly by multiplying text length by pattern length.
Time Complexity: O(n * m)
This means the time needed grows proportionally to the product of the text size and the pattern size.
[X] Wrong: "The algorithm always checks fewer characters because it stops early on mismatch."
[OK] Correct: Sometimes mismatches happen late, causing many comparisons. In the worst case, it checks almost all characters for each position.
Understanding this simple method helps build a foundation for more efficient pattern search algorithms and shows how to analyze nested loops clearly.
What if we changed the pattern to be very long, almost as long as the text? How would the time complexity change?
Practice
Solution
Step 1: Understand naive matching approach
The naive algorithm tries to match the pattern starting at each position in the text one by one.Step 2: Compare pattern characters at each position
At each position, it compares characters one by one until a mismatch or full match is found.Final Answer:
Check every possible position in the text to see if the pattern matches -> Option AQuick Check:
Naive = Check all positions [OK]
- Thinking naive uses hashing
- Assuming pattern preprocessing is done
- Believing sorting helps in naive matching
text length is n and pattern length is m?Solution
Step 1: Determine valid start positions
The pattern of length m can only start at positions 0 to n-m in the text to avoid overflow.Step 2: Write loop condition
The loop must run from i = 0 up to i <= n - m to cover all valid start indices.Final Answer:
for (int i = 0; i <= n - m; i++) -> Option AQuick Check:
Loop till n-m inclusive [OK]
- Looping till n causes out-of-bound errors
- Looping till m is too short
- Using < instead of <= misses last position
std::string text = "abcabcabc";
std::string pattern = "abc";
int count = 0;
for (int i = 0; i <= text.size() - pattern.size(); i++) {
int j = 0;
while (j < pattern.size() && text[i + j] == pattern[j]) {
j++;
}
if (j == pattern.size()) {
count++;
}
}
std::cout << count << std::endl;Solution
Step 1: Identify pattern occurrences
The pattern "abc" appears at indices 0, 3, and 6 in the text "abcabcabc".Step 2: Count matches in loop
The loop checks each position and increments count when full pattern matches, resulting in count = 3.Final Answer:
3 -> Option DQuick Check:
Pattern occurs 3 times [OK]
- Counting overlapping matches incorrectly
- Stopping after first match
- Off-by-one errors in loop range
std::string text = "hellohello";
std::string pattern = "hello";
for (int i = 0; i < text.size() - pattern.size(); i++) {
int j = 0;
while (j < pattern.size() && text[i + j] == pattern[j]) {
j++;
}
if (j == pattern.size()) {
std::cout << "Pattern found at " << i << std::endl;
}
}Solution
Step 1: Check loop boundary
The loop runs while i < text.size() - pattern.size(), which misses the last valid start position.Step 2: Correct loop condition
It should be i <= text.size() - pattern.size() to include the last possible match position.Final Answer:
Loop condition should be i <= text.size() - pattern.size() -> Option BQuick Check:
Include last start index with <= [OK]
- Using < misses last match
- Swapping indices in comparison
- Assuming code is correct without boundary check
"ababababab" and pattern "abab", how many times will the naive algorithm find the pattern if it counts overlapping occurrences?Solution
Step 1: Identify all overlapping occurrences
The pattern "abab" appears starting at indices 0, 2, 4, and 6 in the text "ababababab".Step 2: Count total matches
Counting all overlapping matches gives a total of 4 occurrences.Final Answer:
4 -> Option CQuick Check:
Overlapping matches count = 4 [OK]
- Ignoring overlapping matches
- Counting only non-overlapping occurrences
- Off-by-one errors in counting
Ready to prove it?
Pass the test and this pattern moves unknown
