You are given two strings s
and sub
. You are also given a 2D character array mappings
where mappings[i] = [oldi, newi]
indicates that you may perform the following operation any number of times:
oldi
of sub
with newi
.Each character in sub
cannot be replaced more than once.
Return true
if it is possible to make sub
a substring of s
by replacing zero or more characters according to mappings
. Otherwise, return false
.
A substring is a contiguous non-empty sequence of characters within a string.
Example 1:
Input: s = "fool3e7bar", sub = "leet", mappings = [["e","3"],["t","7"],["t","8"]] Output: true Explanation: Replace the first 'e' in sub with '3' and 't' in sub with '7'. Now sub = "l3e7" is a substring of s, so we return true.
Example 2:
Input: s = "fooleetbar", sub = "f00l", mappings = [["o","0"]] Output: false Explanation: The string "f00l" is not a substring of s and no replacements can be made. Note that we cannot replace '0' with 'o'.
Example 3:
Input: s = "Fool33tbaR", sub = "leetd", mappings = [["e","3"],["t","7"],["t","8"],["d","b"],["p","b"]] Output: true Explanation: Replace the first and second 'e' in sub with '3' and 'd' in sub with 'b'. Now sub = "l33tb" is a substring of s, so we return true.
Constraints:
1 <= sub.length <= s.length <= 5000
0 <= mappings.length <= 1000
mappings[i].length == 2
oldi != newi
s
and sub
consist of uppercase and lowercase English letters and digits.oldi
and newi
are either uppercase or lowercase English letters or digits.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 strategy for this problem is like trying every single possible replacement allowed by the given rules and then checking if, after those replacements, the substring matches within the main string. It's about exploring all options, even if it takes a long time.
Here's how the algorithm would work step-by-step:
def match_substring_after_replacement_brute_force(
main_string,
substring,
replacements
):
number_of_replacements = len(replacements)
# Generate all possible combinations of replacements
replacement_combinations = [[]]
for replacement_index in range(number_of_replacements):
new_combinations = []
for combination in replacement_combinations:
new_combinations.append(combination)
new_combinations.append(combination + [replacement_index])
replacement_combinations = new_combinations
# Iterate through all combinations of replacements
for combination in replacement_combinations:
modified_string = list(main_string)
# Apply the current combination of replacements
for replacement_index in combination:
character_to_replace = replacements[replacement_index][0]
replacement_character = replacements[replacement_index][1]
for index, character in enumerate(modified_string):
if character == character_to_replace:
modified_string[index] = replacement_character
modified_string = "".join(modified_string)
# Check if the substring exists in the modified string
if substring in modified_string:
# Substring found after replacements
return True
# If substring not found after all replacements
return False
The fastest way to solve this involves transforming the 'mappings' into a more usable form. Then, instead of checking all possible combinations, we efficiently slide a 'window' across the main text, checking for possible matches based on the mappings.
Here's how the algorithm would work step-by-step:
def match_substring_after_replacement(string, sub, mappings):
replacement_map = {}
for original_char, replacements in mappings:
replacement_map[original_char] = set(replacements)
string_length = len(string)
substring_length = len(sub)
for i in range(string_length - substring_length + 1):
# Iterate through possible start positions of the substring.
window = string[i:i + substring_length]
match = True
for j in range(substring_length):
string_char = window[j]
substring_char = sub[j]
# Check if characters match directly or via replacement.
if string_char != substring_char:
if string_char not in replacement_map or substring_char not in replacement_map[string_char]:
match = False
break
# If all characters match, return True.
if match:
return True
return False
Case | How to Handle |
---|---|
s is null or empty | Return false immediately as sub cannot be found in an empty string. |
sub is null or empty | Return true immediately, as an empty substring is always found. |
mappings is null or empty | Perform a direct string comparison between s and sub without replacements. |
s.length < sub.length | Return false immediately since sub cannot be contained in a shorter string. |
mappings contains mappings to the same character (e.g., a -> b, b -> a) | The algorithm should still function correctly, and potentially lead to longer computation, but should not error out. |
sub contains characters not present in mappings at all | The algorithm will treat these characters as requiring an exact match. |
Long s and sub strings to test performance | Ensure the solution's time complexity is considered for large inputs, avoiding brute force approaches if possible. |
Mappings include characters not in s or sub | The algorithm should handle these cases gracefully and simply ignore such mappings during the matching process. |