Unit 5: Naive Pattern Search - Subjective Questions
CSE329 — Prelude To Competitive Coding • Practice Questions with Detailed Answers
20 questions
Define the naïve pattern search problem. Explain the working of the naïve pattern searching algorithm with a suitable example.
Naïve Pattern Search Problem: Given a text T of length n and a pattern P of length m, the goal is to find all occurrences (starting indices) of P within T.
Working:
- The algorithm slides the pattern over the text one character at a time.
- At each shift position
i(from0ton-m), it compares characters of the pattern with the corresponding text characters. - If all
mcharacters match, an occurrence is reported at indexi. - On any mismatch, the pattern is shifted by one position.
Example: T = "AABAACAADAABAABA", P = "AABA".
- Match found at index
0,9, and12.
Time Complexity:
- Worst case: (e.g.,
T = "AAAAA",P = "AAA"). - Best case: .
The algorithm is simple but inefficient for large inputs because it may re-examine characters repeatedly.
Explain the KMP (Knuth-Morris-Pratt) algorithm. How does it improve upon the naïve approach?
KMP Algorithm: An efficient pattern matching algorithm that avoids re-examining previously matched characters by using a precomputed LPS (Longest Prefix Suffix) array.
Key Idea:
- When a mismatch occurs after some matches, KMP uses the LPS array to decide the next position to compare, without moving the text pointer backward.
Steps:
- Preprocess the pattern to build the LPS array (length of the longest proper prefix which is also a suffix for each position).
- Search by scanning the text once, using LPS to skip redundant comparisons on mismatch.
Improvement over Naïve:
- Naïve worst case: .
- KMP: because the text pointer never moves backward.
Example LPS for P = "AAAA" is [0, 1, 2, 3].
Thus KMP guarantees linear time, making it far superior for large texts.
What is the LPS (Longest Prefix Suffix) array in the KMP algorithm? Construct the LPS array for the pattern P = "ABABCABAB".
LPS Array: For each index i of the pattern, LPS[i] stores the length of the longest proper prefix of P[0..i] that is also a suffix of P[0..i]. A proper prefix excludes the whole string itself.
Construction for P = "ABABCABAB":
| Index | Char | LPS |
|---|---|---|
| 0 | A | 0 |
| 1 | B | 0 |
| 2 | A | 1 |
| 3 | B | 2 |
| 4 | C | 0 |
| 5 | A | 1 |
| 6 | B | 2 |
| 7 | A | 3 |
| 8 | B | 4 |
Result: LPS = [0, 0, 1, 2, 0, 1, 2, 3, 4].
The LPS array allows the algorithm to skip comparisons on mismatch, achieving time.
Describe the Rabin-Karp algorithm for pattern searching. What role does hashing play in it?
Rabin-Karp Algorithm: A pattern matching algorithm that uses hashing to compare the pattern with substrings of the text efficiently.
Working:
- Compute the hash of the pattern
Pand the hash of the first window of lengthmin the text. - Slide the window one character at a time, computing a rolling hash for each new window.
- If the hash of a window matches the pattern's hash, perform a character-by-character verification to confirm a real match (to rule out spurious hits / collisions).
Rolling Hash: Using a base d and prime modulus q:
where .
Role of Hashing:
- Reduces string comparison to integer comparison in per window (on average).
- The rolling hash lets us update the hash in constant time instead of recomputing.
Complexity:
- Average and best: .
- Worst case (many collisions): .
It is especially useful for multiple pattern search and plagiarism detection.
Compare the KMP algorithm and the Rabin-Karp algorithm in terms of approach, time complexity, and use cases.
Comparison of KMP and Rabin-Karp:
| Aspect | KMP | Rabin-Karp |
|---|---|---|
| Approach | Uses LPS array to avoid re-comparison | Uses rolling hash to compare windows |
| Preprocessing | Builds LPS array in | Computes pattern hash in |
| Search Time | worst case | average, worst case |
| Overall | guaranteed | average |
| Determinism | Deterministic, no false positives | May have spurious hits requiring verification |
| Best for | Single pattern, guaranteed linear time | Multiple pattern search, plagiarism detection |
Key Points:
- KMP is deterministic and never degrades to quadratic time.
- Rabin-Karp shines when searching for many patterns simultaneously since hashes can be compared quickly.
- KMP requires no probabilistic reasoning, while Rabin-Karp depends on a good hash function to avoid collisions.
What is a Suffix Array? Explain how it is constructed and give its applications.
Suffix Array: A sorted array of all suffixes of a given string. Instead of storing the actual suffixes, it stores the starting indices of the suffixes in lexicographically sorted order.
Example: For S = "banana", the suffixes are:
- 0: banana
- 1: anana
- 2: nana
- 3: ana
- 4: na
- 5: a
Sorted lexicographically: a(5), ana(3), anana(1), banana(0), na(4), nana(2).
Suffix Array = [5, 3, 1, 0, 4, 2].
Construction Methods:
- Naïve: Generate all suffixes and sort them — .
- Efficient: Using prefix-doubling with sorting — or .
Applications:
- Pattern searching using binary search in .
- Finding the longest repeated substring.
- Finding the longest common substring of multiple strings.
- Data compression (used in BWT).
Suffix arrays are a memory-efficient alternative to suffix trees.
Explain how to check if two strings are rotations of each other. Provide an efficient algorithm with an example.
Problem: Given two strings s1 and s2, determine if s2 is a rotation of s1 (e.g., "waterbottle" and "erbottlewat").
Key Insight: s2 is a rotation of s1 if and only if:
s1ands2have the same length, ANDs2is a substring ofs1 + s1(the string concatenated with itself).
Algorithm:
- If
len(s1) != len(s2), returnfalse. - Concatenate:
temp = s1 + s1. - Check whether
s2is a substring oftemp(using KMP for efficiency).
Example:
s1 = "ABCD",s2 = "CDAB".temp = "ABCDABCD"."CDAB"is present intemp→ true.
Complexity:
- Using KMP for substring search: .
- Naïve substring check: .
This elegant trick avoids explicitly generating all rotations.
Describe the Largest Connected Component on a Grid problem. Explain the approach to solve it.
Problem: Given a 2D grid of cells (e.g., 1s representing land and 0s representing water), find the size of the largest connected component (group of adjacent 1s connected horizontally, vertically, and possibly diagonally).
Approach (DFS/BFS Flood Fill):
- Iterate over every cell in the grid.
- When an unvisited cell with value
1is found, start a DFS/BFS traversal. - During traversal, count all connected
1cells and mark them as visited. - Track the maximum count across all components.
Pseudocode (DFS):
function dfs(grid, i, j, visited):
if out of bounds or grid[i][j]==0 or visited[i][j]:
return 0
visited[i][j] = true
count = 1
for each of 8 (or 4) neighbors (di, dj):
count += dfs(grid, i+di, j+dj, visited)
return count
Main Loop:
max = 0
for each cell (i, j):
if grid[i][j]==1 and not visited:
max = maximum(max, dfs(grid, i, j, visited))
Complexity: where R and C are rows and columns, since each cell is visited once.
Connectivity: For 8-directional connectivity, all 8 neighbors are explored; for 4-directional, only up/down/left/right.
Explain how to check whether one string is a substring of another using the naïve approach. Write the pseudocode and analyze its complexity.
Problem: Given a text T (length n) and a pattern P (length m), determine whether P occurs in T.
Naïve Approach:
- Slide the pattern over the text position by position.
- At each position, compare characters until a mismatch or a full match is found.
Pseudocode:
function isSubstring(T, P):
n = length(T)
m = length(P)
for i from 0 to n - m:
j = 0
while j < m and T[i+j] == P[j]:
j = j + 1
if j == m:
return true // pattern found at index i
return false
Complexity Analysis:
- Worst case: — occurs when there are many partial matches (e.g.,
T="AAAAAB",P="AAAB"). - Best case: — mismatch on the first character at every position.
For large inputs, more efficient algorithms like KMP () or Rabin-Karp are preferred.
Explain the concept of Longest Prefix which is also a Suffix (proper prefix-suffix). How is it computed, and why is it important in string algorithms?
Concept: The Longest Proper Prefix which is also a Suffix (LPS) of a string is the longest substring that appears both as a prefix and as a suffix, but is not the entire string itself.
Example: For "abcab":
- Prefixes:
a, ab, abc, abca - Suffixes:
b, ab, cab, bcab - Longest common proper prefix-suffix =
"ab"(length 2).
Computation (using KMP's failure function):
- Initialize
LPS[0] = 0andlen = 0. - Iterate
ifrom1ton-1:- If
P[i] == P[len]:len++,LPS[i] = len. - Else if
len != 0:len = LPS[len-1](fall back). - Else:
LPS[i] = 0.
- If
Importance:
- Core of the KMP algorithm to skip redundant comparisons.
- Used to find shortest period of a string:
period = n - LPS[n-1]. - Helps in problems like string rotation and repeated substring pattern detection.
Complexity: for computation.
Distinguish between a proper prefix, a proper suffix, and a border of a string with examples.
Definitions:
- Prefix: Any substring starting from the first character. For
"abcd": prefixes area, ab, abc, abcd. - Proper Prefix: A prefix that is not the entire string. For
"abcd":a, ab, abc. - Suffix: Any substring ending at the last character. For
"abcd":d, cd, bcd, abcd. - Proper Suffix: A suffix that is not the entire string. For
"abcd":d, cd, bcd. - Border: A string that is both a proper prefix and a proper suffix. For
"abcabc":abcis a border (also""trivially).
Comparison Table:
| Term | Includes whole string? | Position |
|---|---|---|
| Prefix | Yes | Start |
| Proper Prefix | No | Start |
| Suffix | Yes | End |
| Proper Suffix | No | End |
| Border | No | Both ends |
Example: For "aabaa":
- Proper prefixes:
a, aa, aab, aaba. - Proper suffixes:
a, aa, baa, abaa. - Borders:
a,aa→ longest border =aa.
Borders are precisely what the LPS array captures in KMP.
Explain Lexicographical Sorting of strings. How does it differ from numerical sorting? Illustrate with an example.
Lexicographical Sorting: Arranging strings in dictionary order, comparing them character by character based on their character codes (e.g., ASCII/Unicode).
Rules:
- Compare characters at each position from left to right.
- The string with the smaller character at the first differing position comes first.
- If one string is a prefix of another, the shorter string comes first (e.g.,
"app" < "apple").
Example:
Input: ["banana", "apple", "apricot", "app"]
Sorted lexicographically: ["app", "apple", "apricot", "banana"].
Difference from Numerical Sorting:
| Aspect | Lexicographical | Numerical |
|---|---|---|
| Basis | Character codes | Numeric value |
"10" vs "9" |
"10" < "9" (since '1' < '9') |
9 < 10 |
| Data type | Strings | Numbers |
Key Note: Uppercase letters (A-Z: 65–90) come before lowercase (a-z: 97–122) in ASCII, so "Zebra" < "apple" in strict ASCII lexicographical order.
Complexity: Sorting k strings of max length L takes .
Describe how to split a string into substrings based on a delimiter. Discuss different approaches with examples.
Problem: Given a string and a delimiter, break it into a list of substrings (tokens).
Approaches:
1. Iterative scan:
- Traverse the string, accumulate characters into a buffer until the delimiter is found, then push the buffer as a token.
function split(s, delim):
result = []
token = ""
for ch in s:
if ch == delim:
result.add(token)
token = ""
else:
token = token + ch
result.add(token) // last token
return result
2. Built-in library functions:
- Python:
s.split(",") - Java:
s.split(",")(regex-based) - C++: using
stringstreamwithgetline.
Example: "a,b,c,d" split by , → ["a", "b", "c", "d"].
Edge Cases:
- Consecutive delimiters produce empty tokens (
"a,,b"→["a", "", "b"]). - Leading/trailing delimiters produce empty tokens at the ends.
Complexity: where n is the length of the string.
Applications: Parsing CSV data, tokenizing input, processing command-line arguments.
Derive the time complexity of the naïve pattern matching algorithm in the best, average, and worst cases with justification.
Naïve Algorithm Recap: For text of length n and pattern of length m, we try each of the n - m + 1 shift positions and compare up to m characters at each.
Worst Case: .
- Occurs when at each shift, almost all characters match before a final mismatch.
- Example:
T = "AAAAAAAAAB"(n A's),P = "AAAB". Nearlymcomparisons happen at each of then-m+1positions.
Best Case: .
- Occurs when the first character mismatches at nearly every shift position.
- Example:
T = "ABCDEFG",P = "XYZ". Only 1 comparison per shift → aboutncomparisons total.
Average Case: for random text over a reasonably large alphabet.
- On average, mismatches occur quickly, so the expected number of comparisons per shift is a small constant.
Summary Table:
| Case | Complexity | Condition |
|---|---|---|
| Best | Early mismatches | |
| Average | Random data | |
| Worst | Many partial matches |
Space Complexity: — no extra data structures used.
Given the pattern P = "AABAACAABAA", construct the LPS array and trace how KMP uses it during a mismatch.
Building the LPS array for P = "AABAACAABAA":
| i | Char | len | LPS[i] |
|---|---|---|---|
| 0 | A | 0 | 0 |
| 1 | A | 1 | 1 |
| 2 | B | 0 | 0 |
| 3 | A | 1 | 1 |
| 4 | A | 2 | 2 |
| 5 | C | 0 | 0 |
| 6 | A | 1 | 1 |
| 7 | A | 2 | 2 |
| 8 | B | 3 | 3 |
| 9 | A | 4 | 4 |
| 10 | A | 5 | 5 |
Result: LPS = [0, 1, 0, 1, 2, 0, 1, 2, 3, 4, 5].
How KMP Uses LPS on Mismatch:
- Suppose we matched up to pattern index
jand text indexi, thenT[i] != P[j]. - Instead of restarting
j = 0, KMP setsj = LPS[j-1]. - This reuses the already-matched border, so the text pointer
inever moves backward.
Example: If a mismatch occurs at j = 9 (after matching "AABAACAAB"), we set j = LPS[8] = 3, meaning we continue comparing from pattern index 3 without rescanning matched text.
This is what gives KMP its guarantee.
In the Rabin-Karp algorithm, explain the concept of rolling hash and spurious hits. Why is a prime modulus used?
Rolling Hash: A technique to compute the hash of the next window in constant time from the current window's hash, avoiding recomputation from scratch.
Formula: For a window sliding right by one character, with base d and modulus q:
where .
- We remove the leftmost character's contribution and add the new rightmost character.
Spurious Hits (False Positives):
- Two different strings can produce the same hash value (a hash collision).
- When hashes match, Rabin-Karp verifies with a character-by-character comparison to confirm a real match.
- A hash match that is not a real match is called a spurious hit.
Why a Prime Modulus?
- Using a large prime
qminimizes the probability of collisions by distributing hash values more uniformly. - It reduces clustering of hash values, keeping spurious hits rare.
- This keeps the average time complexity close to .
Trade-off: A larger q reduces collisions but must fit within integer limits to avoid overflow.
Explain how a suffix array can be used to search for a pattern in a text. What is the time complexity of this search?
Pattern Search Using Suffix Array:
Since a suffix array stores the starting indices of all suffixes in sorted order, any pattern that occurs in the text is a prefix of some suffix. Because the suffixes are sorted, all occurrences of a pattern appear in a contiguous range of the suffix array.
Algorithm (Binary Search):
- Build the suffix array of the text
T(lengthn) — one-time preprocessing. - To search a pattern
P(lengthm), perform binary search over the suffix array. - At each step, compare
Pwith the suffix at the middle index (comparison takes up to ). - Narrow the search range to find the first and last occurrence.
Example: For T = "banana", suffix array = [5, 3, 1, 0, 4, 2]. To search "ana", binary search locates the suffixes "ana" (index 3) and "anana" (index 1).
Time Complexity:
- Each binary search step does an comparison, with steps.
- Search time: .
- Preprocessing (build): with efficient methods.
Advantage: Once built, multiple pattern queries are answered quickly, making suffix arrays ideal for repeated searches.
Write an algorithm to find the shortest period of a string using the LPS array. Illustrate with the string "abcabcabc".
Concept: The period of a string is the smallest length p such that the string is formed by repeating a block of length p. The LPS array (from KMP) directly gives this.
Formula:
- If
n % period == 0, the string is a perfect repetition of a block of lengthperiod.
Algorithm:
function shortestPeriod(s):
n = length(s)
lps = computeLPS(s)
period = n - lps[n-1]
if n % period == 0:
return period // string is repetition of a block
else:
return n // no smaller repeating period
Illustration for "abcabcabc" (n = 9):
- Compute LPS:
[0, 0, 0, 1, 2, 3, 4, 5, 6]. LPS[8] = 6.period = 9 - 6 = 3.- Since
9 % 3 == 0, the shortest period is 3, and the repeating block is"abc".
Complexity: — dominated by LPS computation.
Application: Detecting repeated patterns, string compression, and the 'Repeated Substring Pattern' problem.
Discuss common problems based on naive pattern search and explain how naïve search can still be useful despite its inefficiency.
Common Problems Solved with Naïve Pattern Search:
- Substring check: Determining whether one string occurs in another.
- Counting occurrences: Finding the number of times a pattern appears in a text.
- Finding all indices: Listing all starting positions of a pattern.
- Anagram/rotation checks: Combined with concatenation tricks.
- Simple find-and-replace: Locating positions before replacement.
- Wildcard matching (basic): Extending naïve comparison to handle
?and*.
Why Naïve Search Is Still Useful:
- Simplicity: Easy to implement and understand — fewer bugs.
- Small inputs: For short texts/patterns, the overhead of preprocessing (KMP/Rabin-Karp) is not worth it.
- Good average performance: On random text over a large alphabet, mismatches occur early, giving near behavior.
- No extra space: Uses auxiliary space, unlike KMP's LPS array.
- Baseline / teaching: Serves as a foundation for understanding advanced algorithms.
Limitation: Its worst case makes it unsuitable for very large texts with repetitive patterns, where KMP or Rabin-Karp should be used.
Write a program logic (pseudocode) to lexicographically sort a list of strings and explain a practice problem where lexicographical ordering is essential.
Pseudocode for Lexicographical Sorting:
function lexSort(arr):
n = length(arr)
for i from 0 to n-2:
for j from 0 to n-2-i:
if compare(arr[j], arr[j+1]) > 0:
swap(arr[j], arr[j+1])
return arr
function compare(a, b):
k = min(length(a), length(b))
for i from 0 to k-1:
if a[i] != b[i]:
return a[i] - b[i] // char code difference
return length(a) - length(b) // shorter comes first
(In practice, use built-in sort with a string comparator for performance.)
Practice Problem — 'Largest Number from Strings':
- Given a list of numeric strings, arrange them to form the largest number.
- Trick: Sort using a custom comparator: for two strings
aandb, placeabeforebifa+b > b+a(comparing concatenations lexicographically). - Example:
["3", "30", "34", "5", "9"]→"9534330".
Why Lexicographical Ordering Matters:
- It provides a consistent, total ordering for strings, essential in dictionaries, autocomplete, ranking, and comparator-based problems where numeric comparison alone fails.
Complexity: with an efficient sort, where L is the maximum string length.
Define the naïve pattern search problem. Explain the working of the naïve pattern searching algorithm with a suitable example.
Naïve Pattern Search Problem: Given a text T of length n and a pattern P of length m, the goal is to find all occurrences (starting indices) of P within T.
Working:
- The algorithm slides the pattern over the text one character at a time.
- At each shift position
i(from0ton-m), it compares characters of the pattern with the corresponding text characters. - If all
mcharacters match, an occurrence is reported at indexi. - On any mismatch, the pattern is shifted by one position.
Example: T = "AABAACAADAABAABA", P = "AABA".
- Match found at index
0,9, and12.
Time Complexity:
- Worst case: (e.g.,
T = "AAAAA",P = "AAA"). - Best case: .
The algorithm is simple but inefficient for large inputs because it may re-examine characters repeatedly.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →