Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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 <= 50000 <= mappings.length <= 1000mappings[i].length == 2oldi != newis and sub consist of uppercase and lowercase English letters and digits.oldi and newi are either uppercase or lowercase English letters or digits.Problem summary: 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: Replace a character 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · String Matching
"fool3e7bar" "leet" [["e","3"],["t","7"],["t","8"]]
"fooleetbar" "f00l" [["o","0"]]
"Fool33tbaR" "leetd" [["e","3"],["t","7"],["t","8"],["d","b"],["p","b"]]
design-add-and-search-words-data-structure)number-of-subarrays-that-match-a-pattern-ii)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2301: Match Substring After Replacement
class Solution {
public boolean matchReplacement(String s, String sub, char[][] mappings) {
Map<Character, Set<Character>> d = new HashMap<>();
for (var e : mappings) {
d.computeIfAbsent(e[0], k -> new HashSet<>()).add(e[1]);
}
int m = s.length(), n = sub.length();
for (int i = 0; i < m - n + 1; ++i) {
boolean ok = true;
for (int j = 0; j < n && ok; ++j) {
char a = s.charAt(i + j), b = sub.charAt(j);
if (a != b && !d.getOrDefault(b, Collections.emptySet()).contains(a)) {
ok = false;
}
}
if (ok) {
return true;
}
}
return false;
}
}
// Accepted solution for LeetCode #2301: Match Substring After Replacement
func matchReplacement(s string, sub string, mappings [][]byte) bool {
d := map[byte]map[byte]bool{}
for _, e := range mappings {
if d[e[0]] == nil {
d[e[0]] = map[byte]bool{}
}
d[e[0]][e[1]] = true
}
for i := 0; i < len(s)-len(sub)+1; i++ {
ok := true
for j := 0; j < len(sub) && ok; j++ {
a, b := s[i+j], sub[j]
if a != b && !d[b][a] {
ok = false
}
}
if ok {
return true
}
}
return false
}
# Accepted solution for LeetCode #2301: Match Substring After Replacement
class Solution:
def matchReplacement(self, s: str, sub: str, mappings: List[List[str]]) -> bool:
d = defaultdict(set)
for a, b in mappings:
d[a].add(b)
for i in range(len(s) - len(sub) + 1):
if all(a == b or a in d[b] for a, b in zip(s[i : i + len(sub)], sub)):
return True
return False
// Accepted solution for LeetCode #2301: Match Substring After Replacement
/**
* [2301] Match Substring After Replacement
*
* 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:
*
* Replace a character 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.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/match-substring-after-replacement/
// discuss: https://leetcode.com/problems/match-substring-after-replacement/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
impl Solution {
pub fn match_replacement(s: String, sub: String, mappings: Vec<Vec<char>>) -> bool {
false
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_2301_example_1() {
let s = "fool3e7bar".to_string();
let sub = "leet".to_string();
let mappings = vec![vec!['e', '3'], vec!['t', '7'], vec!['t', '8']];
let result = true;
assert_eq!(Solution::match_replacement(s, sub, mappings), result);
}
#[test]
#[ignore]
fn test_2301_example_2() {
let s = "fooleetbar".to_string();
let sub = "f00l".to_string();
let mappings = vec![vec!['o', '0']];
let result = false;
assert_eq!(Solution::match_replacement(s, sub, mappings), result);
}
#[test]
#[ignore]
fn test_2301_example_3() {
let s = "Fool33tbaR".to_string();
let sub = "leetd".to_string();
let mappings = vec![
vec!['e', '3'],
vec!['t', '7'],
vec!['t', '8'],
vec!['d', 'b'],
vec!['p', 'b'],
];
let result = true;
assert_eq!(Solution::match_replacement(s, sub, mappings), result);
}
}
// Accepted solution for LeetCode #2301: Match Substring After Replacement
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #2301: Match Substring After Replacement
// class Solution {
// public boolean matchReplacement(String s, String sub, char[][] mappings) {
// Map<Character, Set<Character>> d = new HashMap<>();
// for (var e : mappings) {
// d.computeIfAbsent(e[0], k -> new HashSet<>()).add(e[1]);
// }
// int m = s.length(), n = sub.length();
// for (int i = 0; i < m - n + 1; ++i) {
// boolean ok = true;
// for (int j = 0; j < n && ok; ++j) {
// char a = s.charAt(i + j), b = sub.charAt(j);
// if (a != b && !d.getOrDefault(b, Collections.emptySet()).contains(a)) {
// ok = false;
// }
// }
// if (ok) {
// return true;
// }
// }
// return false;
// }
// }
Use this to step through a reusable interview workflow for this problem.
At each of the n starting positions in the text, compare up to m characters with the pattern. If a mismatch occurs, shift by one and restart. Worst case (e.g., searching "aab" in "aaaa...a") checks m characters at nearly every position: O(n × m).
KMP and Z-algorithm preprocess the pattern in O(m) to build a failure/Z-array, then scan the text in O(n) — never backtracking. Total: O(n + m). Rabin-Karp uses rolling hashes for O(n + m) expected time. All beat the O(n × m) brute force of checking every position.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.