This problem asks whether a given string s
matches a given pattern. Matching means there's a one-to-one mapping between characters in the pattern and non-empty substrings in the string. This requires a backtracking approach because we need to explore all possible mappings.
The solution uses a depth-first search (DFS) or backtracking algorithm to explore all possible mappings between pattern characters and substrings of the input string.
Base Cases:
return true
).return false
).Recursive Step:
d
dictionary, we check if the current substring matches that mapping. If it does, we recursively call dfs
to continue checking the rest of the pattern and string.vis
set (to ensure bijective mapping), we create a new mapping, add the substring to vis
, recursively call dfs
, and then backtrack by removing the mapping and substring to explore other possibilities.Backtracking: The crucial part of this approach is backtracking. After trying a mapping and the recursive call returns false
, we need to undo that mapping to try other possibilities. This is done by removing the mapping from d
and the substring from vis
.
Time Complexity: O(N * M * 2M), where N is the length of the string s
and M is the length of the pattern p
. The exponential part comes from exploring different mappings for each character in the pattern, in the worst case.
Space Complexity: O(M) The space is dominated by the d
dictionary (mapping pattern characters to substrings) and the vis
set (to track used substrings), both of which have sizes at most proportional to the length of the pattern. The recursion depth is also at most proportional to M.
class Solution:
def wordPatternMatch(self, pattern: str, s: str) -> bool:
def dfs(i, j):
if i == m and j == n:
return True
if i == m or j == n or n - j < m - i: # optimization: if remaining string length is less than remaining pattern length
return False
for k in range(j, n):
t = s[j : k + 1]
if d.get(pattern[i]) == t:
if dfs(i + 1, k + 1):
return True
if pattern[i] not in d and t not in vis:
d[pattern[i]] = t
vis.add(t)
if dfs(i + 1, k + 1):
return True
d.pop(pattern[i])
vis.remove(t)
return False
m, n = len(pattern), len(s)
d = {} # Mapping from pattern characters to substrings
vis = set() # Set of used substrings
return dfs(0, 0)
The other languages (Java, C++, Go) follow a similar structure, using their respective data structures for dictionaries and sets. The core logic of the backtracking DFS remains the same.