Given two arrays of strings, s1[] and s2[], count the number of strings in s2[] that occur as either a prefix or a suffix of at least one string in s1[]. Return the total count.
Examples:
Input: s1 = ["cat", "catanddog", "lion"], s2 = ["cat", "dog", "rat"]
Output: 2
Explanation:
"cat" from s2 is a prefix of "catanddog" in s1.
"dog" from s2 is a suffix of "catanddog" in s1.
"rat" is neither a prefix nor a suffix of any string in s1.
Therefore, the count is 2.
Input: s1 = ["jrjiml", "tchetn", "ucrhye", "ynayhy", "cuhffd", "cvgpoiu", "znyadv"]
s2 = ["jr", "ml", "cvgpoi", "gpoiu", "wnmkmluc", "geheqe", "uglxagyl", "uyxdroj"]
Output: 4
Explanation:
"jr" is a prefix of "jrjiml" in s1.
"ml" is a suffix of "jrjiml" in s1.
"cvgpoi" is a prefix of "cvgpoiu" in s1.
"gpoiu" is a suffix of "cvgpoiu" in s1.
The remaining strings in s2 are neither prefixes nor suffixes of any string in s1.
Therefore, the count is 4.
Table of Content
[Naive Approach] Check Every String Pair - O(|s1| * |s2| * L) Time O(1) Space
The idea is to check every string of
s2against every string ofs1. For each pair of strings, we verify whether the string froms2occurs as a prefix or as a suffix of the string froms1. If either condition is satisfied for at least one string ins1, we count that string froms2in the answer.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Checks whether pat is a prefix of string
bool isPrefix(string &str, string &pat)
{
int n = str.length();
int m = pat.length();
if (m > n)
{
return false;
}
for (int i = 0; i < m; i++)
{
if (str[i] != pat[i])
{
return false;
}
}
return true;
}
// Checks whether pat is a suffix of str
bool isSuffix(string &str, string &pat)
{
int n = str.length();
int m = pat.length();
if (m > n)
{
return false;
}
for (int i = 0; i < m; i++)
{
if (str[n - m + i] != pat[i])
{
return false;
}
}
return true;
}
int countPrefixSuffix(vector<string> &s1, vector<string> &s2)
{
int count = 0;
// Check every string pair
for (string &pat : s2)
{
bool found = false;
for (string &str : s1)
{
if (isPrefix(str, pat) || isSuffix(str, pat))
{
found = true;
break;
}
}
if (found)
{
count++;
}
}
return count;
}
// Driver Code
int main()
{
vector<string> s1 = {"jrjiml", "tchetn", "ucrhye", "ynayhy", "cuhffd", "cvgpoiu", "znyadv"};
vector<string> s2 = {"jr", "ml", "cvgpoi", "gpoiu", "wnmkmluc", "geheqe", "uglxagyl", "uyxdroj"};
cout << countPrefixSuffix(s1, s2) << endl;
return 0;
}
import java.util.*;
class GfG {
// Checks whether pat is a prefix of string
static boolean isPrefix(String str, String pat)
{
int n = str.length();
int m = pat.length();
if (m > n) {
return false;
}
for (int i = 0; i < m; i++) {
if (str.charAt(i) != pat.charAt(i)) {
return false;
}
}
return true;
}
// Checks whether pat is a suffix of str
static boolean isSuffix(String str, String pat)
{
int n = str.length();
int m = pat.length();
if (m > n) {
return false;
}
for (int i = 0; i < m; i++) {
if (str.charAt(n - m + i) != pat.charAt(i)) {
return false;
}
}
return true;
}
static int countPrefixSuffix(String[] s1, String[] s2)
{
int count = 0;
// Check every string pair
for (String pat : s2) {
boolean found = false;
for (String str : s1) {
if (isPrefix(str, pat)
|| isSuffix(str, pat)) {
found = true;
break;
}
}
if (found) {
count++;
}
}
return count;
}
// Driver Code
public static void main(String[] args)
{
String[] s1
= { "jrjiml", "tchetn", "ucrhye", "ynayhy",
"cuhffd", "cvgpoiu", "znyadv" };
String[] s2 = { "jr", "ml", "cvgpoi",
"gpoiu", "wnmkmluc", "geheqe",
"uglxagyl", "uyxdroj" };
System.out.println(countPrefixSuffix(s1, s2));
}
}
# Checks whether pat is a prefix of string
def isPrefix(string, pat):
n = len(string)
m = len(pat)
if m > n:
return False
for i in range(m):
if string[i] != pat[i]:
return False
return True
# Checks whether pat is a suffix of str
def isSuffix(string, pat):
n = len(string)
m = len(pat)
if m > n:
return False
for i in range(m):
if string[n - m + i] != pat[i]:
return False
return True
def countPrefixSuffix(s1, s2):
count = 0
# Check every string pair
for pat in s2:
found = False
for string in s1:
if isPrefix(string, pat) or isSuffix(string, pat):
found = True
break
if found:
count += 1
return count
# Driver Code
if __name__ == "__main__":
s1 = ["jrjiml", "tchetn", "ucrhye", "ynayhy",
"cuhffd", "cvgpoiu", "znyadv"]
s2 = ["jr", "ml", "cvgpoi", "gpoiu",
"wnmkmluc", "geheqe", "uglxagyl", "uyxdroj"]
print(countPrefixSuffix(s1, s2))
using System;
using System.Collections.Generic;
class GfG {
// Checks whether pat is a prefix of string
static bool isPrefix(string str, string pat)
{
int n = str.Length;
int m = pat.Length;
if (m > n) {
return false;
}
for (int i = 0; i < m; i++) {
if (str[i] != pat[i]) {
return false;
}
}
return true;
}
// Checks whether pat is a suffix of str
static bool isSuffix(string str, string pat)
{
int n = str.Length;
int m = pat.Length;
if (m > n) {
return false;
}
for (int i = 0; i < m; i++) {
if (str[n - m + i] != pat[i]) {
return false;
}
}
return true;
}
static int countPrefixSuffix(List<string> s1,
List<string> s2)
{
int count = 0;
// Check every string pair
foreach(string pat in s2)
{
bool found = false;
foreach(string str in s1)
{
if (isPrefix(str, pat)
|| isSuffix(str, pat)) {
found = true;
break;
}
}
if (found) {
count++;
}
}
return count;
}
// Driver Code
static void Main()
{
List<string> s1 = new List<string>{
"jrjiml", "tchetn", "ucrhye", "ynayhy",
"cuhffd", "cvgpoiu", "znyadv"
};
List<string> s2 = new List<string>{
"jr", "ml", "cvgpoi", "gpoiu",
"wnmkmluc", "geheqe", "uglxagyl", "uyxdroj"
};
Console.WriteLine(countPrefixSuffix(s1, s2));
}
}
// Checks whether pat is a prefix of string
function isPrefix(str, pat)
{
let n = str.length;
let m = pat.length;
if (m > n) {
return false;
}
for (let i = 0; i < m; i++) {
if (str[i] !== pat[i]) {
return false;
}
}
return true;
}
// Checks whether pat is a suffix of str
function isSuffix(str, pat)
{
let n = str.length;
let m = pat.length;
if (m > n) {
return false;
}
for (let i = 0; i < m; i++) {
if (str[n - m + i] !== pat[i]) {
return false;
}
}
return true;
}
function countPrefixSuffix(s1, s2)
{
let count = 0;
// Check every string pair
for (let pat of s2) {
let found = false;
for (let str of s1) {
if (isPrefix(str, pat) || isSuffix(str, pat)) {
found = true;
break;
}
}
if (found) {
count++;
}
}
return count;
}
// Driver Code
let s1 = [
"jrjiml", "tchetn", "ucrhye", "ynayhy", "cuhffd",
"cvgpoiu", "znyadv"
];
let s2 = [
"jr", "ml", "cvgpoi", "gpoiu", "wnmkmluc", "geheqe",
"uglxagyl", "uyxdroj"
];
console.log(countPrefixSuffix(s1, s2));
Output
4
Time Complexity: O(|s1| * |s2| * L)
Auxiliary Space: O(1)
[Expected Approach] Trie-Based Prefix and Suffix Matching - O((n + m) * L) Time O(n * L) Space
The idea is to use a Trie to efficiently store prefixes of strings. We insert every string from
s1into the Trie so that any traversable path represents a prefix of some string. To handle suffixes, we also insert the reversed form of every string froms1. Then, for each string ins2, we check whether it can be traversed in the Trie (prefix check) or whether its reversed form can be traversed in the Trie (suffix check). If either check succeeds, the string contributes to the answer.
Working of the Approach:
- Build a Trie by inserting every string from
s1, so that every traversable path from the root represents a prefix of some string ins1. - To support suffix matching, also insert the reversed form of every string from
s1into the same Trie. - For each string
strins2, check whetherstrcan be completely traversed in the Trie. If yes, it is a prefix of at least one string ins1. - Also reverse
strand check whether the reversed string can be traversed in the Trie. If yes,stris a suffix of at least one string ins1. - Count every string in
s2for which either the prefix check or the suffix check succeeds.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class TrieNode
{
public:
TrieNode *child[26];
TrieNode()
{
// Initialize all child pointers as null
for (int i = 0; i < 26; i++)
{
child[i] = nullptr;
}
}
};
// Inserts a string into the Trie
void add(TrieNode *root, const string &s)
{
TrieNode *node = root;
for (char ch : s)
{
int idx = ch - 'a';
// Create a new node if the current character
// is not already present in the Trie
if (!node->child[idx])
{
node->child[idx] = new TrieNode();
}
node = node->child[idx];
}
}
// Returns true if all characters of the string
// can be traversed in the Trie
bool find(TrieNode *root, const string &s)
{
TrieNode *node = root;
for (char ch : s)
{
int idx = ch - 'a';
if (!node->child[idx])
{
return false;
}
node = node->child[idx];
}
return true;
}
int countPrefixSuffix(vector<string> &s1, vector<string> &s2)
{
TrieNode *root = new TrieNode();
// Insert every string from s1.
// Any path in the Trie represents a prefix of some string.
// Also insert the reversed string so that suffix checks
// can be converted into prefix checks on reversed strings.
for (const string &str : s1)
{
add(root, str);
string rev = str;
reverse(rev.begin(), rev.end());
add(root, rev);
}
int res = 0;
for (const string &str : s2)
{
string rev = str;
reverse(rev.begin(), rev.end());
// If 'str' exists as a Trie path, it is a prefix
// of at least one string in s1.
//
// If reversed(str) exists as a Trie path, then
// 'str' is a suffix of at least one string in s1.
if (find(root, str) || find(root, rev))
{
res++;
}
}
return res;
}
// Driver Code
int main()
{
vector<string> s1 = {"jrjiml", "tchetn", "ucrhye", "ynayhy", "cuhffd", "cvgpoiu", "znyadv"};
vector<string> s2 = {"jr", "ml", "cvgpoi", "gpoiu", "wnmkmluc", "geheqe", "uglxagyl", "uyxdroj"};
cout << countPrefixSuffix(s1, s2) << endl;
return 0;
}
import java.util.*;
class GfG {
class TrieNode {
TrieNode[] child;
TrieNode()
{
// Initialize all child pointers as null
child = new TrieNode[26];
}
}
// Inserts a string into the Trie
private void add(TrieNode root, String s)
{
TrieNode node = root;
for (char ch : s.toCharArray()) {
int idx = ch - 'a';
// Create a new node if the current
// character is not already present in the
// Trie
if (node.child[idx] == null) {
node.child[idx] = new TrieNode();
}
node = node.child[idx];
}
}
// Returns true if all characters of the string
// can be traversed in the Trie
private boolean find(TrieNode root, String s)
{
TrieNode node = root;
for (char ch : s.toCharArray()) {
int idx = ch - 'a';
if (node.child[idx] == null) {
return false;
}
node = node.child[idx];
}
return true;
}
public int countPrefixSuffix(String[] s1, String[] s2)
{
TrieNode root = new TrieNode();
// Insert every string from s1.
// Any path in the Trie represents a prefix of
// some string. Also insert the reversed string
// so that suffix checks can be converted into
// prefix checks on reversed strings.
for (String str : s1) {
add(root, str);
String rev = new StringBuilder(str)
.reverse()
.toString();
add(root, rev);
}
int res = 0;
for (String str : s2) {
String rev = new StringBuilder(str)
.reverse()
.toString();
// If 'str' exists as a Trie path, it is a
// prefix of at least one string in s1.
//
// If reversed(str) exists as a Trie path,
// then 'str' is a suffix of at least one
// string in s1.
if (find(root, str) || find(root, rev)) {
res++;
}
}
return res;
}
// Driver Code
public static void main(String[] args)
{
String[] s1
= { "jrjiml", "tchetn", "ucrhye", "ynayhy",
"cuhffd", "cvgpoiu", "znyadv" };
String[] s2 = { "jr", "ml", "cvgpoi",
"gpoiu", "wnmkmluc", "geheqe",
"uglxagyl", "uyxdroj" };
GfG obj = new GfG();
System.out.println(obj.countPrefixSuffix(s1, s2));
}
}
class TrieNode:
def __init__(self):
self.child = [None] * 26
# Inserts a string into the Trie
def add(root, s):
node = root
for ch in s:
idx = ord(ch) - ord('a')
if not node.child[idx]:
node.child[idx] = TrieNode()
node = node.child[idx]
# Returns true if all characters of the string
# can be traversed in the Trie
def find(root, s):
node = root
for ch in s:
idx = ord(ch) - ord('a')
if not node.child[idx]:
return False
node = node.child[idx]
return True
def countPrefixSuffix(s1, s2):
root = TrieNode()
for str in s1:
add(root, str)
rev = str[::-1]
add(root, rev)
res = 0
for str in s2:
rev = str[::-1]
if find(root, str) or find(root, rev):
res += 1
return res
# Driver Code
if __name__ == "__main__":
s1 = ["jrjiml", "tchetn", "ucrhye", "ynayhy",
"cuhffd", "cvgpoiu", "znyadv"]
s2 = ["jr", "ml", "cvgpoi", "gpoiu",
"wnmkmluc", "geheqe",
"uglxagyl", "uyxdroj"]
print(countPrefixSuffix(s1, s2))
using System;
class TrieNode {
public TrieNode[] child;
public TrieNode()
{
// Initialize all child pointers as null
child = new TrieNode[26];
}
}
class GfG {
// Inserts a string into the Trie
static void add(TrieNode root, string s)
{
TrieNode node = root;
foreach(char ch in s)
{
int idx = ch - 'a';
// Create a new node if the current character
// is not already present in the Trie
if (node.child[idx] == null) {
node.child[idx] = new TrieNode();
}
node = node.child[idx];
}
}
// Returns true if all characters of the string
// can be traversed in the Trie
static bool find(TrieNode root, string s)
{
TrieNode node = root;
foreach(char ch in s)
{
int idx = ch - 'a';
if (node.child[idx] == null) {
return false;
}
node = node.child[idx];
}
return true;
}
static int countPrefixSuffix(string[] s1, string[] s2)
{
TrieNode root = new TrieNode();
// Insert every string from s1.
// Any path in the Trie represents a prefix of some
// string. Also insert the reversed string so that
// suffix checks can be converted into prefix checks
// on reversed strings.
foreach(string str in s1)
{
add(root, str);
char[] arr = str.ToCharArray();
Array.Reverse(arr);
string rev = new string(arr);
add(root, rev);
}
int res = 0;
foreach(string str in s2)
{
char[] arr = str.ToCharArray();
Array.Reverse(arr);
string rev = new string(arr);
// If 'str' exists as a Trie path, it is a
// prefix of at least one string in s1.
//
// If reversed(str) exists as a Trie path, then
// 'str' is a suffix of at least one string in
// s1.
if (find(root, str) || find(root, rev)) {
res++;
}
}
return res;
}
// Driver Code
static void Main()
{
string[] s1
= { "jrjiml", "tchetn", "ucrhye", "ynayhy",
"cuhffd", "cvgpoiu", "znyadv" };
string[] s2 = { "jr", "ml", "cvgpoi",
"gpoiu", "wnmkmluc", "geheqe",
"uglxagyl", "uyxdroj" };
Console.WriteLine(countPrefixSuffix(s1, s2));
}
}
class TrieNode {
constructor()
{
// Initialize all child pointers as null
this.child = new Array(26).fill(null);
}
}
// Inserts a string into the Trie
function add(root, s)
{
let node = root;
for (let ch of s) {
let idx = ch.charCodeAt(0) - "a".charCodeAt(0);
// Create a new node if the current character
// is not already present in the Trie
if (!node.child[idx]) {
node.child[idx] = new TrieNode();
}
node = node.child[idx];
}
}
// Returns true if all characters of the string
// can be traversed in the Trie
function find(root, s)
{
let node = root;
for (let ch of s) {
let idx = ch.charCodeAt(0) - "a".charCodeAt(0);
if (!node.child[idx]) {
return false;
}
node = node.child[idx];
}
return true;
}
function countPrefixSuffix(s1, s2)
{
let root = new TrieNode();
// Insert every string from s1.
// Any path in the Trie represents a prefix of some
// string. Also insert the reversed string so that
// suffix checks can be converted into prefix checks on
// reversed strings.
for (let str of s1) {
add(root, str);
let rev = str.split("").reverse().join("");
add(root, rev);
}
let res = 0;
for (let str of s2) {
let rev = str.split("").reverse().join("");
// If 'str' exists as a Trie path, it is a prefix
// of at least one string in s1.
//
// If reversed(str) exists as a Trie path, then
// 'str' is a suffix of at least one string in s1.
if (find(root, str) || find(root, rev)) {
res++;
}
}
return res;
}
// Driver Code
let s1 = [
"jrjiml", "tchetn", "ucrhye", "ynayhy", "cuhffd",
"cvgpoiu", "znyadv"
];
let s2 = [
"jr", "ml", "cvgpoi", "gpoiu", "wnmkmluc", "geheqe",
"uglxagyl", "uyxdroj"
];
console.log(countPrefixSuffix(s1, s2));
Output
4
Time Complexity: O((n + m) * L)
Auxiliary Space: O(n * L)