3042. Count Prefix and Suffix Pairs I - Explanation

Problem Link

Description

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.

Constraints:

  • 1 <= words.length <= 50
  • 1 <= words[i].length <= 10
  • words[i] consists only of lowercase English letters.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • String Manipulation - Checking prefixes and suffixes of strings using character comparison or built-in methods
  • Nested Loops - Iterating through all pairs of elements in an array with O(n^2) complexity
  • Trie Data Structure - Understanding how tries store strings and enable efficient prefix lookups (for optimal solution)

1. Brute Force

Intuition

For each pair of indices (i, j) where i < j, we need to check if words[i] is both a prefix and suffix of words[j]. We compare characters at the beginning and end of words[j] with words[i].

Algorithm

  1. Create a helper function that checks if s1 is both a prefix and suffix of s2.
  2. First verify s1 is not longer than s2.
  3. Compare s1 character by character with the start of s2 (prefix check).
  4. Compare s1 character by character with the end of s2 (suffix check).
  5. Iterate through all pairs (i, j) with i < j and count valid prefix-suffix pairs.
class Solution:
    def countPrefixSuffixPairs(self, words: List[str]) -> int:
        def isPrefixAndSuffix(s1, s2):
            if len(s1) > len(s2):
                return False

            for i in range(len(s1)):
                if s1[i] != s2[i]:
                    return False

            j = 0
            for i in range(len(s2) - len(s1), len(s2)):
                if s1[j] != s2[i]:
                    return False
                j += 1

            return True

        res = 0
        for i in range(len(words)):
            for j in range(i + 1, len(words)):
                res += isPrefixAndSuffix(words[i], words[j])
        return res

Time & Space Complexity

  • Time complexity: O(n2m)O(n ^ 2 * m)
  • Space complexity: O(1)O(1)

Where nn is the size of the input array wordswords, and mm is the maximum length of a string.


2. Brute Force (Using Built-In Function)

Intuition

Most programming languages provide built-in methods to check if a string starts with or ends with another string. We can use these to simplify the prefix and suffix checks.

Algorithm

  1. Loop through all pairs (i, j) where i < j.
  2. For each pair, check if words[j] starts with words[i] using the built-in prefix check.
  3. Also check if words[j] ends with words[i] using the built-in suffix check.
  4. If both conditions are true, increment the result counter.
  5. Return the total count.
class Solution:
    def countPrefixSuffixPairs(self, words: List[str]) -> int:
        res = 0

        for i in range(len(words)):
            for j in range(i + 1, len(words)):
                w1, w2 = words[i], words[j]
                if w2.startswith(w1) and w2.endswith(w1):
                    res += 1

        return res

Time & Space Complexity

  • Time complexity: O(n2m)O(n ^ 2 * m)
  • Space complexity: O(1)O(1)

Where nn is the size of the input array wordswords, and mm is the maximum length of a string.


3. Trie

Intuition

We can use a trie where each node is keyed by a pair of characters: one from the prefix and one from the suffix. By processing words in reverse order and storing them in this combined trie, when we look up a word, we find how many previously seen words have it as both prefix and suffix.

Algorithm

  1. Create a trie where each edge is labeled by a pair (prefix char, suffix char).
  2. Process words from the end of the array to the beginning.
  3. For each word, traverse the trie using pairs of (word[i], word[n-1-i]) and count matches.
  4. Then insert the word into the trie, incrementing counts at each node.
  5. Return the total count of matching pairs.
class TrieNode:
    def __init__(self):
        self.children = {}
        self.count = 0

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def add(self, w: str) -> None:
        cur = self.root
        for c1, c2 in zip(w, reversed(w)):
            if (c1, c2) not in cur.children:
                cur.children[(c1, c2)] = TrieNode()
            cur = cur.children[(c1, c2)]
            cur.count += 1

    def count(self, w: str) -> int:
        cur = self.root
        for c1, c2 in zip(w, reversed(w)):
            if (c1, c2) not in cur.children:
                return 0
            cur = cur.children[(c1, c2)]
        return cur.count

class Solution:
    def countPrefixSuffixPairs(self, words: List[str]) -> int:
        res = 0
        root = Trie()

        for w in reversed(words):
            res += root.count(w)
            root.add(w)

        return res

Time & Space Complexity

  • Time complexity: O(nm)O(n * m)
  • Space complexity: O(nm)O(n * m)

Where nn is the size of the input array wordswords, and mm is the maximum length of a string.


Common Pitfalls

Checking Only Prefix or Only Suffix

The condition requires words[i] to be BOTH a prefix AND a suffix of words[j]. Checking only one condition will give incorrect results.

# Wrong: only checks prefix
if w2.startswith(w1):
    res += 1
# Correct: checks both conditions
if w2.startswith(w1) and w2.endswith(w1):
    res += 1

Forgetting the Length Constraint

A string cannot be a prefix or suffix of a shorter string. Failing to check len(s1) <= len(s2) before comparing can lead to index out of bounds errors or false positives.