You are given a pattern and a string s. You need to determine if s matches the pattern. Here, match means there is a one-to-one mapping between letters in the pattern and non-empty substrings in s.
Examples:
Clarifications:
s
or the pattern
string be null or empty? If so, what should I return?pattern
characters limited to lowercase English letters?s
limited to lowercase English letters?pattern
map to an empty string in s
? (No, the problem states non-empty substrings)pattern
has already been mapped to a substring in s
, can it be mapped to a different substring later? (No, the mapping must be one-to-one and consistent).Write a function that takes the pattern
and the string s
as input and returns true
if s
matches the pattern
, and false
otherwise.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this problem is like trying every possible secret code to see if it unlocks a treasure chest. We explore every possible way to match parts of one sequence to the other until we find a matching pattern.
Here's how the algorithm would work step-by-step:
def word_pattern_match(pattern, input_string):
pattern_to_string = {}
string_to_pattern = {}
def backtrack(pattern_index, string_index):
# Base case: both pattern and string are exhausted
if pattern_index == len(pattern) and string_index == len(input_string):
return True
# If either is exhausted while the other isn't, it's a mismatch
if pattern_index == len(pattern) or string_index == len(input_string):
return False
current_pattern_char = pattern[pattern_index]
# Try all possible substrings for the current pattern character
for k in range(string_index + 1, len(input_string) + 1):
substring = input_string[string_index:k]
# If the pattern char already exists, check if it matches
if current_pattern_char in pattern_to_string:
if pattern_to_string[current_pattern_char] != substring:
continue
# Continue matching the rest of the string
if backtrack(pattern_index + 1, k):
return True
# If substring already associated with different pattern
elif substring in string_to_pattern:
continue
# Create new mapping and continue the search
else:
# Add the new mapping to both dictionaries
pattern_to_string[current_pattern_char] = substring
string_to_pattern[substring] = current_pattern_char
# Recurse to check if solution can be found
if backtrack(pattern_index + 1, k):
return True
# Backtrack: Remove the mapping for trying other possibilities
del pattern_to_string[current_pattern_char]
del string_to_pattern[substring]
# If no mapping works, return False
return False
return backtrack(0, 0)
The problem asks us to determine if a pattern of letters can be matched to segments of a string. The optimal approach uses a backtracking strategy, where we explore potential mappings between pattern characters and string segments. If a mapping leads to a dead end, we undo it and try a different one.
Here's how the algorithm would work step-by-step:
def word_pattern_match(pattern, input_string):
pattern_map = {}
string_set = set()
def backtrack(pattern_index, string_index):
if pattern_index == len(pattern) and string_index == len(input_string):
return True
if pattern_index == len(pattern) or string_index == len(input_string):
return False
pattern_character = pattern[pattern_index]
if pattern_character in pattern_map:
mapped_string = pattern_map[pattern_character]
if input_string[string_index:].startswith(mapped_string):
return backtrack(pattern_index + 1, string_index + len(mapped_string))
else:
return False
else:
# Try all possible string segments for the current pattern character
for i in range(string_index, len(input_string)):
substring = input_string[string_index:i + 1]
# Avoid ambiguous mapping to prevent incorrect solutions
if substring in string_set:
continue
# Record our choices before diving deeper
pattern_map[pattern_character] = substring
string_set.add(substring)
if backtrack(pattern_index + 1, i + 1):
return True
# Backtrack to explore other possibilities
del pattern_map[pattern_character]
string_set.remove(substring)
return False
return backtrack(0, 0)
Case | How to Handle |
---|---|
Empty pattern or string | Return true only if both the pattern and the string are empty; otherwise, return false. |
Pattern longer than the string | Return false since each character in the pattern must map to a non-empty substring. |
Pattern with repeating characters and string with inconsistent substrings | Backtracking will eventually fail to find a consistent mapping, returning false. |
A character in the pattern maps to an empty string | Explicitly forbid mapping to empty strings during the backtracking search. |
One-to-many mapping; a string substring is mapped to multiple pattern characters | Use a hash map to track existing mappings and reject conflicting assignments. |
Many-to-one mapping; a pattern character is mapped to different substrings | Use a hash set to track substrings already mapped to, preventing reusing them for another character. |
String contains only repeating characters and pattern demands multiple different substrings | Backtracking explores all possibilities and will fail to find valid mapping to the pattern characters, thus returning false. |
Maximum input size exceeding recursion depth limits | The backtracking algorithm might hit the maximum recursion depth limit for large input sizes, so consider iterative approaches for larger input or optimize backtracking logic. |