Given two strings, one is a text string, text, and the other is a pattern string, pattern. Find and print the indices of all the occurrences of the pattern string within the text string. Use 0-based indexing for the indices, and ensure that the indices are in ascending order. If the pattern does not occur in the text, return an empty list.
Implement this solution using the Knuth-Morris-Pratt (KMP) algorithm for efficient pattern matching.
Input: text = "abracadabra", pattern = "abra"
Output: 0 7
Expalanation : The pattern "abra" appears at the 0st and 7th positions in the text "abracadabra".
Input: text = "abcabcabc", pattern = "abc"
Output: 0 3 6
Expalanation : The pattern "abc" appears at the 0st, 3th, and 6th positions in the text "abcabcabc".
Input: text = "daad", pattern = "aa"
It stores the length of the longest proper prefix of the string that is also a suffix for each prefix of the string.
LPS[i]:
the length of the longest prefix of P that is also a suffix for the substring P[0..i].
[0, 0, 1, 2, 3, 0]
.
We can use the concept of Longest Prefix Suffix (LPS) array to find the indices of pattern P in the text T. The idea is to form a new string S by combining the pattern and text with a delimiter ('$') in between such that:
S = P + '$' + T
.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
// Compute the Longest prefix suffix array for the combined string
vector<int> computeLPS(string s) {
int n = s.size(); // size of string
// To store the longest prefix suffix
vector<int> LPS(n, 0);
// Iterate on the string
for(int i=1; i < n; i++) {
// For all possible lengths
for(int len = 1; len < i; len++) {
if(s.substr(0, len) == s.substr(i-len+1, len))
LPS[i] = len;
}
}
return LPS; // Return the computed LPS array
}
public:
// Function to find all indices of pattern in text
vector<int> search(string pattern, string text) {
string s = pattern + '$' + text; // Combined string
// Function call to find the Z array for the combined string
vector<int> LPS = computeLPS(s);
// Length of pattern and text
int n = text.size(), m = pattern.size();
// To store the result
vector<int> ans;
// Iterate on the combined string after the delimiter
for(int i = m+1; i < s.size(); i++) {
if(LPS[i] == m) ans.push_back(i - 2*m);
}
return ans;
}
};
int main() {
string text = "xyzabxyzabxyz";
string pattern = "xyz";
// Creating an instance of the solution class
Solution sol;
// Function call to find all indices of pattern in text
vector<int> ans = sol.search(text, pattern);
for(auto ind : ans) cout << ind << " ";
return 0;
}
import java.util.*;
class Solution {
// Compute the Longest prefix suffix array for the combined string
private int[] computeLPS(String s) {
int n = s.length(); // size of string
// To store the longest prefix suffix
int[] LPS = new int[n];
// Iterate on the string
for (int i = 1; i < n; i++) {
// For all possible lengths
for (int len = 1; len < i; len++) {
if (s.substring(0, len).equals(s.substring(i - len + 1, i + 1))) {
LPS[i] = len;
}
}
}
return LPS; // Return the computed LPS array
}
// Function to find all indices of pattern in text
public List<Integer> search(String pattern, String text) {
String s = pattern + '$' + text; // Combined string
// Function call to find the LPS array for the combined string
int[] LPS = computeLPS(s);
// Length of pattern and text
int n = text.length(), m = pattern.length();
// To store the result
List<Integer> ans = new ArrayList<>();
// Iterate on the combined string after the delimiter
for (int i = m + 1; i < s.length(); i++) {
if (LPS[i] == m) ans.add(i - 2 * m);
}
return ans;
}
public static void main(String[] args) {
String text = "xyzabxyzabxyz";
String pattern = "xyz";
// Creating an instance of the solution class
Solution sol = new Solution();
// Function call to find all indices of pattern in text
List<Integer> ans = sol.search(pattern, text);
for (int ind : ans) {
System.out.print(ind + " ");
}
}
}
class Solution:
# Compute the Longest prefix suffix array for the combined string
def computeLPS(self, s):
n = len(s) # size of string
# To store the longest prefix suffix
LPS = [0] * n
# Iterate on the string
for i in range(1, n):
# For all possible lengths
for length in range(1, i):
if s[:length] == s[i - length + 1: i + 1]:
LPS[i] = length
return LPS # Return the computed LPS array
# Function to find all indices of pattern in text
def search(self, pattern, text):
s = pattern + '$' + text # Combined string
# Function call to find the LPS array for the combined string
LPS = self.computeLPS(s)
# Length of pattern and text
n, m = len(text), len(pattern)
# To store the result
ans = []
# Iterate on the combined string after the delimiter
for i in range(m + 1, len(s)):
if LPS[i] == m:
ans.append(i - 2 * m)
return ans
if __name__ == "__main__":
text = "xyzabxyzabxyz"
pattern = "xyz"
# Creating an instance of the solution class
sol = Solution()
# Function call to find all indices of pattern in text
ans = sol.search(pattern, text)
print(" ".join(map(str, ans)))
class Solution {
// Compute the Longest prefix suffix array for the combined string
computeLPS(s) {
const n = s.length; // size of string
// To store the longest prefix suffix
const LPS = new Array(n).fill(0);
// Iterate on the string
for (let i = 1; i < n; i++) {
// For all possible lengths
for (let len = 1; len < i; len++) {
if (s.substring(0, len) === s.substring(i - len + 1, i + 1)) {
LPS[i] = len;
}
}
}
return LPS; // Return the computed LPS array
}
// Function to find all indices of pattern in text
search(pattern, text) {
const s = pattern + '$' + text; // Combined string
// Function call to find the LPS array for the combined string
const LPS = this.computeLPS(s);
// Length of pattern and text
const n = text.length, m = pattern.length;
// To store the result
const ans = [];
// Iterate on the combined string after the delimiter
for (let i = m + 1; i < s.length; i++) {
if (LPS[i] === m) {
ans.push(i - 2 * m);
}
}
return ans;
}
}
// Main code
const text = "xyzabxyzabxyz";
const pattern = "xyz";
// Creating an instance of the solution class
const sol = new Solution();
// Function call to find all indices of pattern in text
const ans = sol.search(pattern, text);
console.log(ans.join(" "));
Time Complexity: O(N3), where N is the length of the combined string
Computing the LPS array takes O(N3) time and finding the matches requires iterating on the LPS taking O(N) time.
Space Complexity: O(N), to store the LPS array.
The optimal solution includes forming the LPS array using an optimized approach. To optimize the LPS computation, we must:
s[i] == s[j]
, it means that we have successfully extended an existing prefix-suffix match.
s[i] ≠ s[j]
, we need to backtrack j without resetting it to 0 immediately. Instead, we use LPS[j-1] to find the next possible matching prefix. LPS[j-1]
tells us the longest proper prefix that is also a suffix of the prefix ending at index j-1. If s[i] does not match s[j], then instead of rechecking from j = 0, we can check from LPS[j-1]
. s[i] != s[j]
, it means that we must backtrack j to LPS[j-1]
to find the next possible matching prefix. This leverages the previously computed LPS values to avoid redundant comparisons.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
// Compute the Longest prefix suffix array for the combined string
vector<int> computeLPS(string s) {
int n = s.size(); // size of string
// To store the longest prefix suffix
vector<int> LPS(n, 0);
int i = 1, j = 0;
// Iterate on the string
while(i < n) {
// If the character matches
if(s[i] == s[j]) {
LPS[i] = j+1;
i++, j++;
}
// Otherwise
else {
// Trace back j pointer till it does not match
while(j > 0 && s[i] != s[j]) {
j = LPS[j-1];
}
// If a match is found
if(s[i] == s[j]) {
LPS[i] = j+1;
j++;
}
i += 1;
}
}
return LPS; // Return the computed LPS array
}
public:
// Function to find all indices of pattern in text
vector<int> search(string pattern, string text) {
string s = pattern + '$' + text; // Combined string
// Function call to find the Z array for the combined string
vector<int> LPS = computeLPS(s);
// Length of pattern and text
int n = text.size(), m = pattern.size();
// To store the result
vector<int> ans;
// Iterate on the combined string after the delimiter
for(int i = m+1; i < s.size(); i++) {
if(LPS[i] == m) ans.push_back(i - 2*m);
}
return ans;
}
};
int main() {
string text = "xyzabxyzabxyz";
string pattern = "xyz";
// Creating an instance of the solution class
Solution sol;
// Function call to find all indices of pattern in text
vector<int> ans = sol.search(pattern, text);
for(auto ind : ans) cout << ind << " ";
return 0;
}
import java.util.*;
class Solution {
// Compute the Longest prefix suffix array for the combined string
private int[] computeLPS(String s) {
int n = s.length(); // size of string
// To store the longest prefix suffix
int[] LPS = new int[n];
int i = 1, j = 0;
// Iterate on the string
while (i < n) {
// If the character matches
if (s.charAt(i) == s.charAt(j)) {
LPS[i] = j + 1;
i++;
j++;
}
// Otherwise
else {
// Trace back j pointer till it does not match
while (j > 0 && s.charAt(i) != s.charAt(j)) {
j = LPS[j - 1];
}
// If a match is found
if (s.charAt(i) == s.charAt(j)) {
LPS[i] = j + 1;
j++;
}
i += 1;
}
}
return LPS; // Return the computed LPS array
}
// Function to find all indices of pattern in text
public List<Integer> search(String pattern, String text) {
String s = pattern + '$' + text; // Combined string
// Function call to find the LPS array for the combined string
int[] LPS = computeLPS(s);
// Length of pattern and text
int n = text.length(), m = pattern.length();
// To store the result
List<Integer> ans = new ArrayList<>();
// Iterate on the combined string after the delimiter
for (int i = m + 1; i < s.length(); i++) {
if (LPS[i] == m) ans.add(i - 2 * m);
}
return ans;
}
}
class Main {
public static void main(String[] args) {
String text = "xyzabxyzabxyz";
String pattern = "xyz";
// Creating an instance of the solution class
Solution sol = new Solution();
// Function call to find all indices of pattern in text
List<Integer> ans = sol.search(pattern, text);
for (int ind : ans) System.out.print(ind + " ");
}
}
class Solution:
# Compute the Longest prefix suffix array for the combined string
def computeLPS(self, s):
n = len(s) # size of string
# To store the longest prefix suffix
LPS = [0] * n
i, j = 1, 0
# Iterate on the string
while i < n:
# If the character matches
if s[i] == s[j]:
LPS[i] = j + 1
i += 1
j += 1
# Otherwise
else:
# Trace back j pointer till it does not match
while j > 0 and s[i] != s[j]:
j = LPS[j - 1]
# If a match is found
if s[i] == s[j]:
LPS[i] = j + 1
j += 1
i += 1
return LPS # Return the computed LPS array
# Function to find all indices of pattern in text
def search(self, pattern, text):
s = pattern + '$' + text # Combined string
# Function call to find the LPS array for the combined string
LPS = self.computeLPS(s)
# Length of pattern and text
n, m = len(text), len(pattern)
# To store the result
ans = []
# Iterate on the combined string after the delimiter
for i in range(m + 1, len(s)):
if LPS[i] == m:
ans.append(i - 2 * m)
return ans
if __name__ == "__main__":
text = "xyzabxyzabxyz"
pattern = "xyz"
# Creating an instance of the solution class
sol = Solution()
# Function call to find all indices of pattern in text
ans = sol.search(pattern, text)
print(*ans)
class Solution {
// Compute the Longest prefix suffix array for the combined string
computeLPS(s) {
let n = s.length; // size of string
// To store the longest prefix suffix
let LPS = new Array(n).fill(0);
let i = 1, j = 0;
// Iterate on the string
while (i < n) {
// If the character matches
if (s[i] === s[j]) {
LPS[i] = j + 1;
i++;
j++;
}
// Otherwise
else {
// Trace back j pointer till it does not match
while (j > 0 && s[i] !== s[j]) {
j = LPS[j - 1];
}
// If a match is found
if (s[i] === s[j]) {
LPS[i] = j + 1;
j++;
}
i += 1;
}
}
return LPS; // Return the computed LPS array
}
// Function to find all indices of pattern in text
search(pattern, text) {
let s = pattern + '$' + text; // Combined string
// Function call to find the LPS array for the combined string
let LPS = this.computeLPS(s);
// Length of pattern and text
let n = text.length, m = pattern.length;
// To store the result
let ans = [];
// Iterate on the combined string after the delimiter
for (let i = m + 1; i < s.length; i++) {
if (LPS[i] === m) ans.push(i - 2 * m);
}
return ans;
}
}
// Main Execution
let text = "xyzabxyzabxyz";
let pattern = "xyz";
// Creating an instance of the solution class
let sol = new Solution();
// Function call to find all indices of pattern in text
let ans = sol.search(pattern, text);
console.log(...ans);
Time Complexity: O(N), where N is the length of the combined string
Computing the LPS array takes O(N) time:
Q: How does the LPS array optimize the search?
A: Instead of restarting after a mismatch, LPS shifts the pattern intelligently to align with previous matches.
Q: What happens when a pattern contains repeated characters?
A: The LPS table efficiently skips redundant character comparisons, making KMP well-suited for such cases.
Q: What happens if we need to find overlapping occurrences?
A: KMP already supports overlapping matches without modification.
Q: Can KMP be modified for approximate pattern matching?
A: Yes, by allowing tolerances for mismatches using dynamic programming.