Talentd
FleetCode
Make and follow sheets.Sign in

Count Prefix and Suffix Pairs II solution and explanation

HardArrayHash FunctionRolling HashString7 min read28.2% acceptanceUpdated 9 Apr 2026
Count Prefix and Suffix Pairs I & II - Leetcode 3042 & 3045 - PythonNeetCodeIO · 20:54 · 12K viewsWatch on YouTube
8 more videos on this problem
  1. Count Prefix and Suffix Pairs I & II - Leetcode 3042 & 3045 - PythonNeetCodeIO · 20:54 · 12K views
  2. 3045 & 3042. Count Prefix and Suffix Pairs I & II | Trie | Hashing | Complete IntuitionAryan Mittal · 42:49 · 5K views
  3. 3045. Count Prefix and Suffix Pairs II | Weekly Leetcode 385codingMohan · 46:04 · 1K views
  4. 3045. Count Prefix and Suffix Pairs II (Leetcode Hard)Programming Live with Larry · 23:56 · 627 views
  5. 【每日一题】LeetCode 3045. Count Prefix and Suffix Pairs IIHuifeng Guan · 35:10 · 432 views
  6. Day 35/75: Problem Name: Count Prefix and Suffix Pairs II | Leetcode | #75DaysHardLC | Weekly 385CodeInsanity · 12:16 · 204 views
  7. LeetCode 3045 - Count Prefix and Suffix Pairs II - PythonAlpha-Code · 22:48 · 149 views
  8. 3045. Count Prefix and Suffix Pairs II | Trie | Google | LeetcodeHimani · 17:15 · 77 views
  9. Leetcode Weekly Contest 385 Problem Count Prefix and Suffix Pairs II Leetcode Contact 385 Problem Dcode conqueror · 0:07 · 11 views

The problem

You are given a 0-indexed string array words.

Let's define a boolean function isPrefixAndSuffix that takes two strings, str1 and str2:

  • isPrefixAndSuffix(str1, str2) returns true if str1 is both a prefix and a suffix of str2, and false otherwise.

For example, isPrefixAndSuffix("aba", "ababa") is true because "aba" is a prefix of "ababa" and also a suffix, but isPrefixAndSuffix("abc", "abcd") is false.

Return an integer denoting the number of index pairs (i, j) such that i < j, and isPrefixAndSuffix(words[i], words[j]) is true.

Example 1:

Input: words = ["a","aba","ababa","aa"]
Output: 4
Explanation: In this example, the counted index pairs are:
i = 0 and j = 1 because isPrefixAndSuffix("a", "aba") is true.
i = 0 and j = 2 because isPrefixAndSuffix("a", "ababa") is true.
i = 0 and j = 3 because isPrefixAndSuffix("a", "aa") is true.
i = 1 and j = 2 because isPrefixAndSuffix("aba", "ababa") is true.
Therefore, the answer is 4.

Example 2:

Input: words = ["pa","papa","ma","mama"]
Output: 2
Explanation: In this example, the counted index pairs are:
i = 0 and j = 1 because isPrefixAndSuffix("pa", "papa") is true.
i = 2 and j = 3 because isPrefixAndSuffix("ma", "mama") is true.
Therefore, the answer is 2.  

Example 3:

Input: words = ["abab","ab"]
Output: 0
Explanation: In this example, the only valid index pair is i = 0 and j = 1, and isPrefixAndSuffix("abab", "ab") is false.
Therefore, the answer is 0.

Constraints:

  • 1 <= words.length <= 105
  • 1 <= words[i].length <= 105
  • words[i] consists only of lowercase English letters.
  • The sum of the lengths of all words[i] does not exceed 5 * 105.

Approach overview

Problem Overview: You are given an array of strings. Count pairs (i, j) such that i < j and words[i] is both a prefix and a suffix of words[j]. The challenge comes from large input sizes, which makes naive pairwise string comparison too slow.

Approach 1: Brute Force String Comparison (O(n^2 · m) time, O(1) space)

Check every pair of indices (i, j) with i < j. For each pair, verify whether words[i] matches both the prefix and suffix of words[j]. Prefix comparison checks the first len(words[i]) characters, while suffix comparison checks the last len(words[i]). Each comparison costs up to O(m) where m is the string length. This results in O(n^2 · m) total time. The approach is straightforward and useful for understanding the requirement, but it becomes impractical when the number of strings grows.

Approach 2: Optimized Rolling Hash Matching (O(totalLength) average time, O(n) space)

Instead of comparing strings character by character, compute rolling hash values for prefixes and suffixes of each word. While scanning a word, build prefix and suffix hashes simultaneously. Whenever the prefix hash equals the suffix hash for length k, the substring is both a prefix and suffix candidate. Store previously seen words in a hash map keyed by their hash value. A constant-time hash lookup tells you how many earlier strings match the current prefix–suffix substring. This converts expensive string comparisons into fast integer comparisons.

The key idea: a string that is both prefix and suffix must have identical hash values from both directions. With precomputed powers and incremental hashing, each character contributes once, giving near linear complexity across all strings.

Some implementations also use a trie or prefix tree to track prefixes efficiently, but hashing usually gives simpler code and excellent performance.

Recommended for interviews: Start by explaining the brute force solution to show you understand the pair condition and prefix/suffix check. Then move to the hashing optimization. Interviewers expect you to eliminate repeated string comparisons using hashing or a string processing technique. The optimized approach demonstrates knowledge of rolling hashes and efficient substring matching.

Approach 1: Brute Force Approach

This approach involves iterating through each pair of strings and checking if one is both a prefix and suffix of the other. Although this might be simple to implement, it can be computationally expensive for large inputs.

def is_prefix_and_suffix(str1, str2):
    if len(str1) > len(str2):
        return False
    return str2.startswith(str1) and str2.endswith(str1)


def count_prefix_and_suffix_pairs(words):
    count = 0
    for i in range(len(words)):
        for j in range(i + 1, len(words)):
            if is_prefix_and_suffix(words[i], words[j]):
                count += 1
    return count


words = ["a", "aba", "ababa", "aa"]
print(count_prefix_and_suffix_pairs(words))
import java.util.*;

public class Main {
    public static boolean isPrefixAndSuffix(String str1, String str2) {
        if (str1.length() > str2.length()) return false;
        return str2.startsWith(str1) && str2.endsWith(str1);
    }

    public static int countPrefixAndSuffixPairs(String[] words) {
        int count = 0;
        for (int i = 0; i < words.length; i++) {
            for (int j = i + 1; j < words.length; j++) {
                if (isPrefixAndSuffix(words[i], words[j]))
                    count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        String[] words = {"a", "aba", "ababa", "aa"};
        System.out.println(countPrefixAndSuffixPairs(words));
    }
}
#include <iostream>
#include <vector>

bool isPrefixAndSuffix(const std::string& str1, const std::string& str2) {
    if (str1.length() > str2.length()) return false;
    return str2.compare(0, str1.length(), str1) == 0 &&
           str2.compare(str2.length() - str1.length(), str1.length(), str1) == 0;
}

int countPrefixAndSuffixPairs(const std::vector<std::string>& words) {
    int count = 0;
    for (size_t i = 0; i < words.size(); ++i) {
        for (size_t j = i + 1; j < words.size(); ++j) {
            if (isPrefixAndSuffix(words[i], words[j]))
                ++count;
        }
    }
    return count;
}

int main() {
    std::vector<std::string> words = {"a", "aba", "ababa", "aa"};
    std::cout << countPrefixAndSuffixPairs(words) << std::endl;
    return 0;
}
function isPrefixAndSuffix(str1, str2) {
    if (str1.length > str2.length) return false;
    return str2.startsWith(str1) && str2.endsWith(str1);
}

function countPrefixAndSuffixPairs(words) {
    let count = 0;
    for (let i = 0; i < words.length; i++) {
        for (let j = i + 1; j < words.length; j++) {
            if (isPrefixAndSuffix(words[i], words[j]))
                count++;
        }
    }
    return count;
}

const words = ["a", "aba", "ababa", "aa"];
console.log(countPrefixAndSuffixPairs(words));
#include <stdio.h>
#include <string.h>

int isPrefixAndSuffix(char* str1, char* str2) {
    int len1 = strlen(str1);
    int len2 = strlen(str2);
    if (len1 > len2) return 0;
    return strncmp(str1, str2, len1) == 0 && strncmp(str1, str2 + len2 - len1, len1) == 0;
}

int countPrefixAndSuffixPairs(char** words, int wordsSize) {
    int count = 0;
    for (int i = 0; i < wordsSize; i++) {
        for (int j = i + 1; j < wordsSize; j++) {
            if (isPrefixAndSuffix(words[i], words[j]))
                count++;
        }
    }
    return count;
}

int main() {
    char* words[] = {"a", "aba", "ababa", "aa"};
    int result = countPrefixAndSuffixPairs(words, 4);
    printf("%d\n", result);
    return 0;
}
using System;

public class MainClass {
    public static bool IsPrefixAndSuffix(string str1, string str2) {
        if (str1.Length > str2.Length) return false;
        return str2.StartsWith(str1) && str2.EndsWith(str1);
    }

    public static int CountPrefixAndSuffixPairs(string[] words) {
        int count = 0;
        for (int i = 0; i < words.Length; i++) {
            for (int j = i + 1; j < words.Length; j++) {
                if (IsPrefixAndSuffix(words[i], words[j]))
                    count++;
            }
        }
        return count;
    }

    public static void Main(string[] args) {
        string[] words = {"a", "aba", "ababa", "aa"};
        Console.WriteLine(CountPrefixAndSuffixPairs(words));
    }
}

Python's startswith and endswith methods are utilized to determine if one string is both prefix and suffix of another. The solution involves nested loops to find all such pairs.

Complexity

Time Complexity: O(n^2 * m). Space Complexity: O(1).

Try this approach in the editor

Approach 2: Optimized Approach Using Hashing

This approach utilizes hashing to optimize the searching of potential prefix and suffix pairs. By hashing potential prefixes and suffixes, we can reduce redundant checks, improving efficiency.

def count_prefix_and_suffix_pairs(words):
    count = 0
    seen = set()
    for j in range(len(words)):
        for i in range(j):
            if len(words[i]) <= len(words[j]):
                if words[i] in seen:
                    count += 1
        seen.add(words[j])
    return count

words = ["a", "aba", "ababa", "aa"]
print(count_prefix_and_suffix_pairs(words))
import java.util.*;

public class Main {
    public static int countPrefixAndSuffixPairs(String[] words) {
        int count = 0;
        Set<String> seen = new HashSet<>();
        for (int j = 0; j < words.length; j++) {
            for (int i = 0; i < j; i++) {
                if (words[i].length() <= words[j].length()) {
                    if (seen.contains(words[i])) {
                        count++;
                    }
                }
            }
            seen.add(words[j]);
        }
        return count;
    }

    public static void main(String[] args) {
        String[] words = {"a", "aba", "ababa", "aa"};
        System.out.println(countPrefixAndSuffixPairs(words));
    }
}
#include <iostream>
#include <unordered_set>
#include <string>
#include <vector>

int countPrefixAndSuffixPairs(const std::vector<std::string>& words) {
    int count = 0;
    std::unordered_set<std::string> seen;
    for (size_t j = 0; j < words.size(); ++j) {
        int lenJ = words[j].length();
        for (size_t i = 0; i < j; ++i) {
            if (words[i].length() <= lenJ) {
                if (seen.find(words[i]) != seen.end()) {
                    ++count;
                }
            }
        }
        seen.insert(words[j]);
    }
    return count;
}

int main() {
    std::vector<std::string> words = {"a", "aba", "ababa", "aa"};
    std::cout << countPrefixAndSuffixPairs(words) << std::endl;
    return 0;
}
function countPrefixAndSuffixPairs(words) {
    let count = 0;
    const seen = new Set();
    for (let j = 0; j < words.length; j++) {
        for (let i = 0; i < j; i++) {
            if (words[i].length <= words[j].length) {
                if (seen.has(words[i])) {
                    count++;
                }
            }
        }
        seen.add(words[j]);
    }
    return count;
}

const words = ["a", "aba", "ababa", "aa"];
console.log(countPrefixAndSuffixPairs(words));
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXN 100000
#define HASHSIZE 100003

typedef struct Node {
    char *word;
    struct Node *next;
} Node;

Node *hashTable[HASHSIZE];

unsigned int hash(const char *word) {
    unsigned int h = 0;
    while (*word) h = (h * 31 + *word++) % HASHSIZE;
    return h;
}

void insert(const char *word) {
    unsigned int h = hash(word);
    Node *node = (Node *)malloc(sizeof(Node));
    node->word = strdup(word);
    node->next = hashTable[h];
    hashTable[h] = node;
}

int search(const char *word) {
    unsigned int h = hash(word);
    Node *p = hashTable[h];
    while (p) {
        if (strcmp(p->word, word) == 0) return 1;
        p = p->next;
    }
    return 0;
}

void freeHashTable() {
    for (int i = 0; i < HASHSIZE; i++) {
        Node *node = hashTable[i];
        while (node) {
            Node *temp = node;
            node = node->next;
            free(temp->word);
            free(temp);
        }
    }
}

int countPrefixAndSuffixPairs(char **words, int wordsSize) {
    memset(hashTable, 0, sizeof(hashTable));
    int count = 0;
    for (int j = 0; j < wordsSize; j++) {
        int lenJ = strlen(words[j]);
        for (int i = 0; i < wordsSize; i++) {
            if (i < j && strlen(words[i]) <= lenJ) {
                if (search(words[i])) 
                    count++;
            }
        }
        insert(words[j]);
    }
    freeHashTable();
    return count;
}

int main() {
    char *words[] = {"a", "aba", "ababa", "aa"};
    int result = countPrefixAndSuffixPairs(words, 4);
    printf("%d\n", result);
    return 0;
}
using System;
using System.Collections.Generic;

public class MainClass {
    public static int CountPrefixAndSuffixPairs(string[] words) {
        int count = 0;
        HashSet<string> seen = new HashSet<string>();
        for (int j = 0; j < words.Length; j++) {
            for (int i = 0; i < j; i++) {
                if (words[i].Length <= words[j].Length) {
                    if (seen.Contains(words[i])) {
                        count++;
                    }
                }
            }
            seen.Add(words[j]);
        }
        return count;
    }

    public static void Main(string[] args) {
        string[] words = {"a", "aba", "ababa", "aa"};
        Console.WriteLine(CountPrefixAndSuffixPairs(words));
    }
}

Using Python's set improves efficiency by leveraging fast membership testing and insertions, significantly reducing time complexity.

Complexity

Time Complexity: O(n * m). Space Complexity: O(n * m).

Try this approach in the editor

Approach 3: Naive Brute Force Approach

In this approach, we iterate over all possible pairs of indices (i, j) with i < j and check whether the word at index i is both a prefix and a suffix of the word at index j. Although simple, this approach is inefficient for large datasets due to its O(n*m) complexity, where n is the number of words and m is the average length of the words.

def isPrefixAndSuffix(prefix, string):
    if len(prefix) > len(string):
        return False
    return string.startswith(prefix) and string.endswith(prefix)


def countPrefixSuffixPairs(words):
    count = 0
    for i in range(len(words)):
        for j in range(i + 1, len(words)):
            if isPrefixAndSuffix(words[i], words[j]):
                count += 1
    return count


words = ["a", "aba", "ababa", "aa"]
print(countPrefixSuffixPairs(words))
import java.util.*;

public class Main {
    public static boolean isPrefixAndSuffix(String prefix, String str) {
        int lenPref = prefix.length();
        int lenStr = str.length();
        if (lenPref > lenStr) return false;
        return str.startsWith(prefix) && str.endsWith(prefix);
    }

    public static int countPrefixSuffixPairs(String[] words) {
        int count = 0;
        for (int i = 0; i < words.length; i++) {
            for (int j = i + 1; j < words.length; j++) {
                if (isPrefixAndSuffix(words[i], words[j])) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        String[] words = {"a", "aba", "ababa", "aa"};
        System.out.println(countPrefixSuffixPairs(words));
    }
}
#include <iostream>
#include <vector>
using namespace std;

bool isPrefixAndSuffix(const string &prefix, const string &str) {
    int lenPref = prefix.length();
    int lenStr = str.length();
    if (lenPref > lenStr) return false;
    return (str.substr(0, lenPref) == prefix && str.substr(lenStr - lenPref) == prefix);
}

int countPrefixSuffixPairs(const vector<string> &words) {
    int count = 0;
    for (size_t i = 0; i < words.size(); ++i) {
        for (size_t j = i + 1; j < words.size(); ++j) {
            if (isPrefixAndSuffix(words[i], words[j])) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    vector<string> words = {"a", "aba", "ababa", "aa"};
    cout << countPrefixSuffixPairs(words) << endl;
    return 0;
}
function isPrefixAndSuffix(prefix, string) {
    if (prefix.length > string.length) return false;
    return string.startsWith(prefix) && string.endsWith(prefix);
}

function countPrefixSuffixPairs(words) {
    let count = 0;
    for (let i = 0; i < words.length; i++) {
        for (let j = i + 1; j < words.length; j++) {
            if (isPrefixAndSuffix(words[i], words[j])) {
                count++;
            }
        }
    }
    return count;
}

const words = ["a", "aba", "ababa", "aa"];
console.log(countPrefixSuffixPairs(words));
#include <stdio.h>
#include <string.h>

int isPrefixAndSuffix(char *prefix, char *str) {
    int lenPref = strlen(prefix);
    int lenStr = strlen(str);
    if (lenPref > lenStr) return 0;
    for (int i = 0; i < lenPref; ++i) {
        if (prefix[i] != str[i] || prefix[i] != str[lenStr - lenPref + i]) return 0;
    }
    return 1;
}

int countPrefixSuffixPairs(char *words[], int wordsSize) {
    int count = 0;
    for (int i = 0; i < wordsSize; ++i) {
        for (int j = i + 1; j < wordsSize; ++j) {
            if (isPrefixAndSuffix(words[i], words[j])) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    char *words[] = {"a", "aba", "ababa", "aa"};
    int wordsSize = sizeof(words) / sizeof(words[0]);
    printf("%d\n", countPrefixSuffixPairs(words, wordsSize));
    return 0;
}
using System;

class Program {
    public static bool IsPrefixAndSuffix(string prefix, string str) {
        int lenPref = prefix.Length;
        int lenStr = str.Length;
        if (lenPref > lenStr) return false;
        return str.StartsWith(prefix) && str.EndsWith(prefix);
    }

    public static int CountPrefixSuffixPairs(string[] words) {
        int count = 0;
        for (int i = 0; i < words.Length; i++) {
            for (int j = i + 1; j < words.Length; j++) {
                if (IsPrefixAndSuffix(words[i], words[j])) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void Main() {
        string[] words = { "a", "aba", "ababa", "aa" };
        Console.WriteLine(CountPrefixSuffixPairs(words));
    }
}

The Python solution employs the list data structure to store words and validates each pair using pre-existing string methods `startswith` and `endswith` for prefix and suffix checks respectively.

Complexity

The time complexity is O(n^2 * m) given the nested loops and string operation costs, and the space complexity is O(1).

Try this approach in the editor

Approach 4: Optimized Approach Using KMP Algorithm

The Knuth-Morris-Pratt (KMP) algorithm can be used to find if a string is a prefix and suffix by preprocessing the pattern and avoiding redundant checks. The prefix function used in KMP gives valuable insight into repeated patterns in a string.

def compute_lps_array(pat):
    lps = [0] * len(pat)
    length = 0  # length of the previous longest prefix suffix
    i = 1
    while i < len(pat):
        if pat[i] == pat[length]:
            length += 1
            lps[i] = length
            i += 1
        else:
            if length != 0:
                length = lps[length - 1]
            else:
                lps[i] = 0
                i += 1
    return lps


def is_prefix_and_suffix_kmp(prefix, string):
    if len(prefix) > len(string):
        return False

    combined = prefix + "#" + string
    lps = compute_lps_array(combined)

    return lps[-1] >= len(prefix)


def count_prefix_suffix_pairs_kmp(words):
    count = 0
    for i in range(len(words)):
        for j in range(i + 1, len(words)):
            if is_prefix_and_suffix_kmp(words[i], words[j]):
                count += 1
    return count


words = ["a", "aba", "ababa", "aa"]
print(count_prefix_suffix_pairs_kmp(words))
import java.util.*;

public class Main {
    private static void computeLPSArray(String pattern, int[] lps) {
        int len = 0;
        int i = 1;
        lps[0] = 0;
        while (i < pattern.length()) {
            if (pattern.charAt(i) == pattern.charAt(len)) {
                len++;
                lps[i++] = len;
            } else {
                if (len != 0) {
                    len = lps[len - 1];
                } else {
                    lps[i++] = 0;
                }
            }
        }
    }

    private static boolean isPrefixAndSuffixKMP(String prefix, String str) {
        if (prefix.length() > str.length()) return false;

        String combined = prefix + "#" + str;
        int[] lps = new int[combined.length()];
        computeLPSArray(combined, lps);

        return lps[combined.length() - 1] >= prefix.length();
    }

    public static int countPrefixSuffixPairsKMP(String[] words) {
        int count = 0;
        for (int i = 0; i < words.length; i++) {
            for (int j = i + 1; j < words.length; j++) {
                if (isPrefixAndSuffixKMP(words[i], words[j])) {
                    count++;
                }
            }
        }
        return count;
    }

    public static void main(String[] args) {
        String[] words = { "a", "aba", "ababa", "aa" };
        System.out.println(countPrefixSuffixPairsKMP(words));
    }
}
#include <iostream>
#include <vector>
#include <string>
using namespace std;

void computeLPSArray(const string &str, vector<int> &lps) {
    int M = str.length();
    int length = 0;
    lps[0] = 0;
    int i = 1;
    while (i < M) {
        if (str[i] == str[length]) {
            length++;
            lps[i++] = length;
        } else {
            if (length != 0) {
                length = lps[length - 1];
            } else {
                lps[i++] = 0;
            }
        }
    }
}

bool isPrefixAndSuffixKMP(const string &prefix, const string &str) {
    int lenPref = prefix.length();
    int lenStr = str.length();
    if (lenPref > lenStr) return false;

    string combined = prefix + "#" + str;
    vector<int> lps(combined.length(), 0);
    computeLPSArray(combined, lps);

    return lps.back() >= lenPref;
}

int countPrefixSuffixPairsKMP(const vector<string> &words) {
    int count = 0;
    for (size_t i = 0; i < words.size(); ++i) {
        for (size_t j = i + 1; j < words.size(); ++j) {
            if (isPrefixAndSuffixKMP(words[i], words[j])) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    vector<string> words = {"a", "aba", "ababa", "aa"};
    cout << countPrefixSuffixPairsKMP(words) << endl;
    return 0;
}
function computeLPSArray(pattern) {
    let lps = Array(pattern.length).fill(0);
    let len = 0;
    let i = 1;
    while (i < pattern.length) {
        if (pattern[i] == pattern[len]) {
            len++;
            lps[i++] = len;
        } else {
            if (len != 0) {
                len = lps[len - 1];
            } else {
                lps[i++] = 0;
            }
        }
    }
    return lps;
}

function isPrefixAndSuffixKMP(prefix, str) {
    if (prefix.length > str.length) return false;

    let combined = prefix + '#' + str;
    let lps = computeLPSArray(combined);

    return lps[lps.length - 1] >= prefix.length;
}

function countPrefixSuffixPairsKMP(words) {
    let count = 0;
    for (let i = 0; i < words.length; i++) {
        for (let j = i + 1; j < words.length; j++) {
            if (isPrefixAndSuffixKMP(words[i], words[j])) {
                count++;
            }
        }
    }
    return count;
}

let words = ["a", "aba", "ababa", "aa"];
console.log(countPrefixSuffixPairsKMP(words));
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

void computeLPSArray(const char* str, int M, int* lps) {
    int length = 0;
    int i = 1;
    lps[0] = 0;
    while (i < M) {
        if (str[i] == str[length]) {
            length++;
            lps[i] = length;
            i++;
        } else {
            if (length != 0) {
                length = lps[length - 1];
            } else {
                lps[i] = 0;
                i++;
            }
        }
    }
}

bool isPrefixAndSuffixKMP(const char* prefix, const char* str) {
    int lenPrefix = strlen(prefix);
    int lenStr = strlen(str);
    if (lenPrefix > lenStr) return false;

    // Compute the concatenated string selfCheck
    char concat[strlen(prefix) + strlen(str) + 1];
    sprintf(concat, "%s#%s", prefix, str);

    // Compute LPS array for the concatenated string
    int lps[strlen(concat)];
    computeLPSArray(concat, strlen(concat), lps);

    // The prefix and suffix must match completely in the concatenated string
    return lps[strlen(concat) - 1] >= lenPrefix;
}

int countPrefixSuffixPairsKMP(char* words[], int wordsSize) {
    int count = 0;
    for (int i = 0; i < wordsSize; ++i) {
        for (int j = i + 1; j < wordsSize; ++j) {
            if (isPrefixAndSuffixKMP(words[i], words[j])) {
                count++;
            }
        }
    }
    return count;
}

int main() {
    char* words[] = {"a", "aba", "ababa", "aa"};
    int wordsSize = sizeof(words) / sizeof(words[0]);
    printf("%d\n", countPrefixSuffixPairsKMP(words, wordsSize));
    return 0;
}
using System;

class Program {
    static void ComputeLPSArray(string pattern, int[] lps) {
        int len = 0;
        lps[0] = 0;
        int i = 1;
        while (i < pattern.Length) {
            if (pattern[i] == pattern[len]) {
                len++;
                lps[i++] = len;
            } else {
                if (len != 0) {
                    len = lps[len - 1];
                } else {
                    lps[i++] = 0;
                }
            }
        }
    }

    static bool IsPrefixAndSuffixKMP(string prefix, string str) {
        if (prefix.Length > str.Length) return false;

        string combined = prefix + "#" + str;
        int[] lps = new int[combined.Length];
        ComputeLPSArray(combined, lps);

        return lps[combined.Length - 1] >= prefix.Length;
    }

    static int CountPrefixSuffixPairsKMP(string[] words) {
        int count = 0;
        for (int i = 0; i < words.Length; i++) {
            for (int j = i + 1; j < words.Length; j++) {
                if (IsPrefixAndSuffixKMP(words[i], words[j])) {
                    count++;
                }
            }
        }
        return count;
    }

    static void Main() {
        string[] words = { "a", "aba", "ababa", "aa" };
        Console.WriteLine(CountPrefixSuffixPairsKMP(words));
    }
}

The Python implementation leverages KMP's LPS array to efficiently match prefix and suffix conditions in a scalable manner.

Complexity

With O(n * m) time complexity, the method efficiently handles inputs using an O(m) space LPS array.

Try this approach in the editor

Approach 5: Trie

We can treat each string s in the string array as a list of character pairs, where each character pair (s[i], s[m - i - 1]) represents the ith character pair of the prefix and suffix of string s.

We can use a trie to store all the character pairs, and then for each string s, we search for all the character pairs (s[i], s[m - i - 1]) in the trie, and add their counts to the answer.

The time complexity is O(n times m), and the space complexity is O(n times m). Here, n and m are the lengths of words and the maximum length of the strings, respectively.

class Node {
    children: Map<number, Node> = new Map<number, Node>();
    cnt: number = 0;
}

function countPrefixSuffixPairs(words: string[]): number {
    let ans: number = 0;
    const trie: Node = new Node();
    for (const s of words) {
        let node: Node = trie;
        const m: number = s.length;
        for (let i: number = 0; i < m; ++i) {
            const p: number = s.charCodeAt(i) * 32 + s.charCodeAt(m - i - 1);
            if (!node.children.has(p)) {
                node.children.set(p, new Node());
            }
            node = node.children.get(p)!;
            ans += node.cnt;
        }
        ++node.cnt;
    }
    return ans;
}
type Node struct {
	children map[int]*Node
	cnt      int
}

func countPrefixSuffixPairs(words []string) (ans int64) {
	trie := &Node{children: make(map[int]*Node)}
	for _, s := range words {
		node := trie
		m := len(s)
		for i := 0; i < m; i++ {
			p := int(s[i])*32 + int(s[m-i-1])
			if _, ok := node.children[p]; !ok {
				node.children[p] = &Node{children: make(map[int]*Node)}
			}
			node = node.children[p]
			ans += int64(node.cnt)
		}
		node.cnt++
	}
	return
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Brute Force Approach

Time Complexity: O(n^2 * m). Space Complexity: O(1).

Optimized Approach Using Hashing

Time Complexity: O(n * m). Space Complexity: O(n * m).

Naive Brute Force Approach

The time complexity is O(n^2 * m) given the nested loops and string operation costs, and the space complexity is O(1).

Optimized Approach Using KMP Algorithm

With O(n * m) time complexity, the method efficiently handles inputs using an O(m) space LPS array.

Trie–

Complexity analysis

ApproachTimeSpaceWhen to Use
Brute Force String ComparisonO(n^2 · m)O(1)Small input sizes or when demonstrating the basic idea in interviews
Rolling Hash with Hash MapO(total string length)O(n)Large inputs where repeated prefix/suffix comparisons must be avoided

Frequently asked questions

Is Count Prefix and Suffix Pairs II easy or hard?

Count Prefix and Suffix Pairs II is classified as a Hard problem. The difficulty comes from large constraints and the need to replace naive comparisons with hashing or advanced string matching techniques.

Count Prefix and Suffix Pairs II Python/Java solution

Python and Java solutions typically implement rolling hash with precomputed powers and a hash map storing previously seen strings. As each word is processed, prefix and suffix hashes are compared and matched against the map. This keeps the algorithm close to linear time.

How to solve Count Prefix and Suffix Pairs II in O(n)?

Process each word while maintaining rolling hashes for its prefixes and suffixes. When both hashes match for a certain length, the substring is both prefix and suffix. Use a hash map storing previously processed words by hash value to count matches in constant time. This avoids pairwise comparisons and scales linearly with the total input size.

What is the best approach for Count Prefix and Suffix Pairs II?

The most efficient approach uses rolling hash combined with a hash map. While scanning each word, compute prefix and suffix hashes and check when they match. Each matching prefix-suffix substring can be looked up in a map of previously seen words, allowing the pair count to be updated in constant time.

Is Count Prefix and Suffix Pairs II asked at Google/Amazon/Meta?

Problems involving prefix-suffix matching, rolling hashes, and trie structures appear frequently in interviews at companies like Google, Amazon, and Meta. Variants of this problem test knowledge of string hashing, substring matching, and efficient counting techniques.

What data structure is used in Count Prefix and Suffix Pairs II?

Common solutions use a hash map with rolling hash values to represent strings efficiently. Some implementations also use a trie to track prefixes and detect matches while inserting characters. Both approaches aim to avoid repeated string comparisons.

What is the time complexity of Count Prefix and Suffix Pairs II?

The brute force solution runs in O(n^2 · m) where n is the number of strings and m is the average string length. The optimized rolling hash approach processes characters once and performs constant-time hash lookups, resulting in roughly O(total string length) time with O(n) extra space.

Comments

Now write it yourselfClose this page and solve Count Prefix and Suffix Pairs II in the editor, without looking.
Practise Count Prefix and Suffix Pairs II