Search Patterns with Dot in a Dictionary

Last Updated : 9 Sep, 2026

Given two arrays of strings d[] and words[], where d[] contains a set of dictionary words and words[] contains search patterns. For each pattern in words[], determine whether it matches any word in d[].

A pattern matches a dictionary word if:

  • Both strings have the same length.
  • Every character matches at the same position.
  • A (.) dot in the pattern can match any single lowercase alphabet.

Return the number of patterns in words[] that match at least one word in d[].

Examples:

Input: d[] = ["bad", "dad", "mad"], words[] = ["pad", "bad", ".ad", "b.."]
Output: 3
Explanation:
"pad" → No match
"bad" → Matches "bad"
".ad" → Matches "bad", "dad", and "mad"
"b.." → Matches "bad"
Therefore, 3 patterns have at least one matching word.

Input: d[] = ["cat", "car", "dog", "door"], words[] = ["c.t", "ca.", "d..", "do.", "....."]
Output: 4
Explanation:
"c.t" → Matches "cat"
"ca." → Matches "cat" or "car"
"d.." → Matches "dog"
"do." → Matches "dog"
"....." → No match because there is no 5-letter dictionary word.
Therefore, 4 patterns have at least one matching word.

Try It Yourself
redirect icon

[Naive Approach] Check Every Pattern With Every Word

The simplest idea is to check every pattern from words[] against every word in d[].

Two strings match if they have the same length and, at every position, either their characters are equal or the character in the pattern is dot(.).

  • Initialize count = 0.
  • For every pattern in words[], compare it with every word in d[].
  • If their lengths differ, skip that word.
  • Compare both strings character by character. If pattern[j] == dot(.), it matches any character.
  • Otherwise, pattern[j] must be equal to dictionary word[j].
  • If all characters match, increment count and move to the next pattern.
  • Return count.
C++
#include <bits/stdc++.h>
using namespace std;

int countMatches(vector<string> &d, vector<string> &words)
{
    // Stores the number of matched patterns.
    int count = 0;

    // Traverse every pattern in words[].
    for (int i = 0; i < words.size(); i++)
    {
        string pattern = words[i];

        // Check the current pattern against every
        // word in the dictionary.
        for (int j = 0; j < d.size(); j++)
        {
            string word = d[j];

            // Patterns and words must have the same length.
            if (pattern.size() != word.size())
                continue;

            // Assume that the pattern matches the word.
            bool match = true;

            // Compare characters at each position.
            for (int k = 0; k < pattern.size(); k++)
            {
                // A dot can match any character.
                if (pattern[k] == '.')
                    continue;

                // Otherwise, characters must be equal.
                if (pattern[k] != word[k])
                {
                    match = false;
                    break;
                }
            }

            // If the pattern matches this word,
            // count it and move to the next pattern.
            if (match)
            {
                count++;
                break;
            }
        }
    }

    return count;
}

int main()
{
    vector<string> d = {"bad", "dad", "mad"};
    vector<string> words = {"pad", "bad", ".ad", "b.."};

    cout << countMatches(d, words) << endl;

    return 0;
}
Java
class GFG {
    static int countMatches(String[] d, String[] words)
    {
        // Stores the number of matched patterns.
        int count = 0;

        // Traverse every pattern in words[].
        for (int i = 0; i < words.length; i++) {
            String pattern = words[i];

            // Check the current pattern against every
            // word in the dictionary.
            for (int j = 0; j < d.length; j++) {
                String word = d[j];

                // Patterns and words must have the same
                // length.
                if (pattern.length() != word.length())
                    continue;

                // Assume that the pattern matches the word.
                boolean match = true;

                // Compare characters at each position.
                for (int k = 0; k < pattern.length(); k++) {

                    // A dot can match any character.
                    if (pattern.charAt(k) == '.')
                        continue;

                    // Otherwise, characters must be equal.
                    if (pattern.charAt(k)
                        != word.charAt(k)) {
                        match = false;
                        break;
                    }
                }

                // If the pattern matches this word,
                // count it and move to the next pattern.
                if (match) {
                    count++;
                    break;
                }
            }
        }

        return count;
    }

    public static void main(String[] args)
    {
        String[] d = { "bad", "dad", "mad" };
        String[] words = { "pad", "bad", ".ad", "b.." };

        System.out.println(countMatches(d, words));
    }
}
Python
def countMatches(d, words):
    # Stores the number of matched patterns.
    count = 0

    # Traverse every pattern in words[].
    for i in range(len(words)):
        pattern = words[i]

        # Check the current pattern against every
        # word in the dictionary.
        for j in range(len(d)):
            word = d[j]

            # Patterns and words must have the same length.
            if len(pattern) != len(word):
                continue

            # Assume that the pattern matches the word.
            match = True

            # Compare characters at each position.
            for k in range(len(pattern)):

                # A dot can match any character.
                if pattern[k] == '.':
                    continue

                # Otherwise, characters must be equal.
                if pattern[k] != word[k]:
                    match = False
                    break

            # If the pattern matches this word,
            # count it and move to the next pattern.
            if match:
                count += 1
                break

    return count


# Driver Code
if __name__ == "__main__":
    d = ["bad", "dad", "mad"]
    words = ["pad", "bad", ".ad", "b.."]

    print(countMatches(d, words))
C#
using System;

class GFG {
    static int countMatches(string[] d, string[] words)
    {
        // Stores the number of matched patterns.
        int count = 0;

        // Traverse every pattern in words[].
        for (int i = 0; i < words.Length; i++) {
            string pattern = words[i];

            // Check the current pattern against every
            // word in the dictionary.
            for (int j = 0; j < d.Length; j++) {
                string word = d[j];

                // Patterns and words must have the same
                // length.
                if (pattern.Length != word.Length)
                    continue;

                // Assume that the pattern matches the word.
                bool match = true;

                // Compare characters at each position.
                for (int k = 0; k < pattern.Length; k++) {
                    // A dot can match any character.
                    if (pattern[k] == '.')
                        continue;

                    // Otherwise, characters must be equal.
                    if (pattern[k] != word[k]) {
                        match = false;
                        break;
                    }
                }

                // If the pattern matches this word,
                // count it and move to the next pattern.
                if (match) {
                    count++;
                    break;
                }
            }
        }

        return count;
    }

    static void Main()
    {
        string[] d = { "bad", "dad", "mad" };
        string[] words = { "pad", "bad", ".ad", "b.." };

        Console.WriteLine(countMatches(d, words));
    }
}
JavaScript
function countMatches(d, words)
{
    // Stores the number of matched patterns.
    let count = 0;

    // Traverse every pattern in words[].
    for (let i = 0; i < words.length; i++) {
        let pattern = words[i];

        // Check the current pattern against every
        // word in the dictionary.
        for (let j = 0; j < d.length; j++) {
            let word = d[j];

            // Patterns and words must have the same length.
            if (pattern.length !== word.length)
                continue;

            // Assume that the pattern matches the word.
            let match = true;

            // Compare characters at each position.
            for (let k = 0; k < pattern.length; k++) {

                // A dot can match any character.
                if (pattern[k] === ".")
                    continue;

                // Otherwise, characters must be equal.
                if (pattern[k] !== word[k]) {
                    match = false;
                    break;
                }
            }

            // If the pattern matches this word,
            // count it and move to the next pattern.
            if (match) {
                count++;
                break;
            }
        }
    }

    return count;
}

// Driver Code
let d = [ "bad", "dad", "mad" ];
let words = [ "pad", "bad", ".ad", "b.." ];

console.log(countMatches(d, words));

Output
3

Time Complexity: O(m * n * L), where m is the no of words in d, n is the no of patterns in words and L is the maximum length string.
Auxiliary Space: O(1)

[Expected Approach] - Using Trie Data Structure

We store all words from d[] in a Trie.

While searching a pattern from words[], we do the following,

  • Normal characters follow their corresponding Trie edge.
  • A dot(.) can match any character, so we explore all possible child nodes.
  • If we reach the end of the pattern at a complete word, the pattern matches.
  • Create a Trie where each node has 26 children and an isEnd flag.
  • Insert every word from d[] into the Trie.
  • For every pattern in words[], start a DFS from the Trie root.
  • At each position:
    If the pattern character is a normal alphabet, follow its corresponding child.
    If it is dot(.), recursively explore all existing child nodes.
  • If the entire pattern is consumed and the current node is an end-of-word node, return true.
  • If the pattern matches at least one dictionary word, increment count.
C++
#include <bits/stdc++.h>
using namespace std;

class TrieNode
{
  public:
    TrieNode *children[26];

    // True if a complete word ends at this node.
    bool isEnd;

    TrieNode()
    {
        isEnd = false;

        // Initially, no child nodes exist.
        for (int i = 0; i < 26; i++)
            children[i] = nullptr;
    }
};

// Inserts a word into the Trie.
void insertWord(TrieNode *root, string &word)
{
    // Start from the root.
    TrieNode *curr = root;

    // Traverse every character of the word.
    for (char ch : word)
    {
        int index = ch - 'a';

        // Create the child node if it does not exist.
        if (curr->children[index] == nullptr)
            curr->children[index] = new TrieNode();

        // Move to the next node.
        curr = curr->children[index];
    }

    // Mark the last node as the end of a word.
    curr->isEnd = true;
}

// Searches a pattern in the Trie.
bool searchPattern(TrieNode *curr, string &pattern, int index)
{
    // If the complete pattern has been processed,
    // check whether a complete word ends here.
    if (index == pattern.size())
        return curr->isEnd;

    // Get the current character of the pattern.
    char ch = pattern[index];

    // If the current character is a dot,
    // it can match any alphabet.
    if (ch == '.')
    {
        // Try every possible child node.
        for (int i = 0; i < 26; i++)
        {
            // Continue only if the child exists.
            if (curr->children[i] != nullptr)
            {
                // Recursively search the remaining pattern.
                if (searchPattern(curr->children[i], pattern, index + 1))
                    return true;
            }
        }

        // No child produced a valid match.
        return false;
    }

    // For a normal character, follow its corresponding child.
    int childIndex = ch - 'a';

    // If the required child does not exist, no match is possible.
    if (curr->children[childIndex] == nullptr)
        return false;

    // Continue searching from the corresponding child.
    return searchPattern(curr->children[childIndex], pattern, index + 1);
}

int countMatches(vector<string> &d, vector<string> &words)
{
    // Create the root of the Trie.
    TrieNode *root = new TrieNode();

    // Insert every dictionary word into the Trie.
    for (string &word : d)
        insertWord(root, word);

    // Stores the number of matched patterns.
    int count = 0;

    // Traverse every pattern in words[].
    for (string &pattern : words)
    {
        // Search the current pattern in the Trie.
        if (searchPattern(root, pattern, 0))
            count++;
    }

    return count;
}

int main()
{
    vector<string> d = {"bad", "dad", "mad"};
    vector<string> words = {"pad", "bad", ".ad", "b.."};

    cout << countMatches(d, words) << endl;

    return 0;
}
Java
class TrieNode {
    TrieNode[] children;

    // True if a complete word ends at this node.
    boolean isEnd;

    TrieNode()
    {
        children = new TrieNode[26];
        isEnd = false;

        // Initially, no child nodes exist.
        for (int i = 0; i < 26; i++)
            children[i] = null;
    }
}

class GFG {
    static void insertWord(TrieNode root, String word)
    {
        // Start from the root.
        TrieNode curr = root;

        // Traverse every character of the word.
        for (char ch : word.toCharArray()) {
            int index = ch - 'a';

            // Create the child node if it does not exist.
            if (curr.children[index] == null)
                curr.children[index] = new TrieNode();

            // Move to the next node.
            curr = curr.children[index];
        }

        // Mark the last node as the end of a word.
        curr.isEnd = true;
    }

    // Searches a pattern in the Trie.
    static boolean searchPattern(TrieNode curr,
                                 String pattern, int index)
    {
        // If the complete pattern has been processed,
        // check whether a complete word ends here.
        if (index == pattern.length())
            return curr.isEnd;

        // Get the current character of the pattern.
        char ch = pattern.charAt(index);

        // If the current character is a dot,
        // it can match any alphabet.
        if (ch == '.') {

            // Try every possible child node.
            for (int i = 0; i < 26; i++) {

                // Continue only if the child exists.
                if (curr.children[i] != null) {

                    // Recursively search the remaining
                    // pattern.
                    if (searchPattern(curr.children[i],
                                      pattern, index + 1))
                        return true;
                }
            }

            // No child produced a valid match.
            return false;
        }

        // For a normal character, follow its corresponding
        // child.
        int childIndex = ch - 'a';

        // If the required child does not exist, no match is
        // possible.
        if (curr.children[childIndex] == null)
            return false;

        // Continue searching from the corresponding child.
        return searchPattern(curr.children[childIndex],
                             pattern, index + 1);
    }

    static int countMatches(String[] d, String[] words)
    {
        // Create the root of the Trie.
        TrieNode root = new TrieNode();

        // Insert every dictionary word into the Trie.
        for (String word : d)
            insertWord(root, word);

        // Stores the number of matched patterns.
        int count = 0;

        // Traverse every pattern in words[].
        for (String pattern : words) {

            // Search the current pattern in the Trie.
            if (searchPattern(root, pattern, 0))
                count++;
        }

        return count;
    }

    public static void main(String[] args)
    {
        String[] d = { "bad", "dad", "mad" };
        String[] words = { "pad", "bad", ".ad", "b.." };

        System.out.println(countMatches(d, words));
    }
}
Python
class TrieNode:
    def __init__(self):
        self.children = [None] * 26

        # True if a complete word ends at this node.
        self.isEnd = False


# Inserts a word into the Trie.
def insertWord(root, word):

    # Start from the root.
    curr = root

    # Traverse every character of the word.
    for ch in word:
        index = ord(ch) - ord('a')

        # Create the child node if it does not exist.
        if curr.children[index] is None:
            curr.children[index] = TrieNode()

        # Move to the next node.
        curr = curr.children[index]

    # Mark the last node as the end of a word.
    curr.isEnd = True


# Searches a pattern in the Trie.
def searchPattern(curr, pattern, index):

    # If the complete pattern has been processed,
    # check whether a complete word ends here.
    if index == len(pattern):
        return curr.isEnd

    # Get the current character of the pattern.
    ch = pattern[index]

    # If the current character is a dot,
    # it can match any alphabet.
    if ch == '.':
        # Try every possible child node.
        for i in range(26):
            # Continue only if the child exists.
            if curr.children[i] is not None:

                # Recursively search the remaining pattern.
                if searchPattern(curr.children[i], pattern, index + 1):
                    return True

        # No child produced a valid match.
        return False

    # For a normal character, follow its corresponding child.
    childIndex = ord(ch) - ord('a')

    # If the required child does not exist, no match is possible.
    if curr.children[childIndex] is None:
        return False

    # Continue searching from the corresponding child.
    return searchPattern(curr.children[childIndex], pattern, index + 1)


def countMatches(d, words):

    # Create the root of the Trie.
    root = TrieNode()

    # Insert every dictionary word into the Trie.
    for word in d:
        insertWord(root, word)

    # Stores the number of matched patterns.
    count = 0

    # Traverse every pattern in words[].
    for pattern in words:
        # Search the current pattern in the Trie.
        if searchPattern(root, pattern, 0):
            count += 1

    return count


# Driver Code
if __name__ == "__main__":
    d = ["bad", "dad", "mad"]
    words = ["pad", "bad", ".ad", "b.."]

    print(countMatches(d, words))
C#
using System;

class TrieNode {
    public TrieNode[] children;

    // True if a complete word ends at this node.
    public bool isEnd;

    public TrieNode()
    {
        children = new TrieNode[26];
        isEnd = false;

        // Initially, no child nodes exist.
        for (int i = 0; i < 26; i++)
            children[i] = null;
    }
}

class GFG {
    // Inserts a word into the Trie.
    static void InsertWord(TrieNode root, string word)
    {
        // Start from the root.
        TrieNode curr = root;

        // Traverse every character of the word.
        foreach(char ch in word)
        {
            int index = ch - 'a';

            // Create the child node if it does not exist.
            if (curr.children[index] == null)
                curr.children[index] = new TrieNode();

            // Move to the next node.
            curr = curr.children[index];
        }

        // Mark the last node as the end of a word.
        curr.isEnd = true;
    }

    // Searches a pattern in the Trie.
    static bool SearchPattern(TrieNode curr, string pattern,
                              int index)
    {
        // If the complete pattern has been processed,
        // check whether a complete word ends here.
        if (index == pattern.Length)
            return curr.isEnd;

        // Get the current character of the pattern.
        char ch = pattern[index];

        // If the current character is a dot,
        // it can match any alphabet.
        if (ch == '.') {

            // Try every possible child node.
            for (int i = 0; i < 26; i++) {

                // Continue only if the child exists.
                if (curr.children[i] != null) {

                    // Recursively search the remaining
                    // pattern.
                    if (SearchPattern(curr.children[i],
                                      pattern, index + 1))
                        return true;
                }
            }

            // No child produced a valid match.
            return false;
        }

        // For a normal character, follow its corresponding
        // child.
        int childIndex = ch - 'a';

        // If the required child does not exist, no match is
        // possible.
        if (curr.children[childIndex] == null)
            return false;

        // Continue searching from the corresponding child.
        return SearchPattern(curr.children[childIndex],
                             pattern, index + 1);
    }

    static int countMatches(string[] d, string[] words)
    {
        // Create the root of the Trie.
        TrieNode root = new TrieNode();

        // Insert every dictionary word into the Trie.
        foreach(string word in d) InsertWord(root, word);

        // Stores the number of matched patterns.
        int count = 0;

        // Traverse every pattern in words[].
        foreach(string pattern in words)
        {
            // Search the current pattern in the Trie.
            if (SearchPattern(root, pattern, 0))
                count++;
        }

        return count;
    }

    static void Main()
    {
        string[] d = { "bad", "dad", "mad" };
        string[] words = { "pad", "bad", ".ad", "b.." };

        Console.WriteLine(countMatches(d, words));
    }
}
JavaScript
class TrieNode {
    constructor()
    {
        this.children = new Array(26).fill(null);

        // True if a complete word ends at this node.
        this.isEnd = false;
    }
}

// Inserts a word into the Trie.
function insertWord(root, word)
{
    // Start from the root.
    let curr = root;

    // Traverse every character of the word.
    for (let ch of word) {
        let index = ch.charCodeAt(0) - "a".charCodeAt(0);

        // Create the child node if it does not exist.
        if (curr.children[index] === null)
            curr.children[index] = new TrieNode();

        // Move to the next node.
        curr = curr.children[index];
    }

    // Mark the last node as the end of a word.
    curr.isEnd = true;
}

// Searches a pattern in the Trie.
function searchPattern(curr, pattern, index)
{
    // If the complete pattern has been processed,
    // check whether a complete word ends here.
    if (index === pattern.length)
        return curr.isEnd;

    // Get the current character of the pattern.
    let ch = pattern[index];

    // If the current character is a dot,
    // it can match any alphabet.
    if (ch === ".") {

        // Try every possible child node.
        for (let i = 0; i < 26; i++) {

            // Continue only if the child exists.
            if (curr.children[i] !== null) {

                // Recursively search the remaining pattern.
                if (searchPattern(curr.children[i], pattern,
                                  index + 1))
                    return true;
            }
        }

        // No child produced a valid match.
        return false;
    }

    // For a normal character, follow its corresponding
    // child.
    let childIndex = ch.charCodeAt(0) - "a".charCodeAt(0);

    // If the required child does not exist, no match is
    // possible.
    if (curr.children[childIndex] === null)
        return false;

    // Continue searching from the corresponding child.
    return searchPattern(curr.children[childIndex], pattern,
                         index + 1);
}

function countMatches(d, words)
{
    // Create the root of the Trie.
    let root = new TrieNode();

    // Insert every dictionary word into the Trie.
    for (let word of d)
        insertWord(root, word);

    // Stores the number of matched patterns.
    let count = 0;

    // Traverse every pattern in words[].
    for (let pattern of words) {
        // Search the current pattern in the Trie.
        if (searchPattern(root, pattern, 0))
            count++;
    }

    return count;
}

// Driver Code
let d = [ "bad", "dad", "mad" ];
let words = [ "pad", "bad", ".ad", "b.." ];

console.log(countMatches(d, words));

Output
3

Time Complexity: O(m * L + n * 26^L), where m is the no of words in d, n is the no of patterns in words and L is the maximum length string.
Auxiliary Space: O(m * L)

Comment