You are given a replacements mapping and a text string that may contain placeholders formatted as %var%, where each var corresponds to a key in the replacements mapping. Each replacement value may itself contain one or more such placeholders. Each placeholder is replaced by the value associated with its corresponding replacement key.
Return the fully substituted text string which does not contain any placeholders.
Example 1:
Input: replacements = [["A","abc"],["B","def"]], text = "%A%_%B%"
Output: "abc_def"
Explanation:
"A" with "abc" and "B" with "def".%A% with "abc" and %B% with "def" in the text."abc_def".Example 2:
Input: replacements = [["A","bce"],["B","ace"],["C","abc%B%"]], text = "%A%_%B%_%C%"
Output: "bce_ace_abcace"
Explanation:
"A" with "bce", "B" with "ace", and "C" with "abc%B%".%A% with "bce" and %B% with "ace" in the text.%C%, substitute %B% in "abc%B%" with "ace" to obtain "abcace"."bce_ace_abcace".
Constraints:
1 <= replacements.length <= 10replacements is a two-element list [key, value], where:
key is a single uppercase English letter.value is a non-empty string of at most 8 characters that may contain zero or more placeholders formatted as %<key>%.text string is formed by concatenating all key placeholders (formatted as %<key>%) randomly from the replacements mapping, separated by underscores.text.length == 4 * replacements.length - 1text or in any replacement value corresponds to a key in the replacements mapping.Problem Overview: You receive a set of substitution rules where a key maps to a string that may reference other keys. Applying substitutions requires expanding each key until only final characters remain. Dependencies between keys form a directed graph, so the main challenge is resolving them in the correct order while avoiding repeated work.
Approach 1: Hash Table + Recursion (DFS with Memoization) (Time: O(n + L), Space: O(n + L))
Store every substitution rule in a hash table so you can resolve a key in constant time. When expanding a key, recursively resolve every referenced token inside its string. This naturally forms a graph where nodes are variables and edges represent dependencies. A depth‑first traversal expands dependencies first, then builds the final resolved string for the current key.
Memoization prevents recomputing the same substitution multiple times. Once a key is fully expanded, cache the result in the hash map. Future lookups return instantly. This converts repeated expansions from exponential behavior into linear work relative to the total size of all strings.
Cycle detection is handled with a recursion stack or visiting set. If a key is encountered while it is still being processed, the rules contain a circular dependency. In most implementations you either reject the input or skip further expansion. Because each key is processed once and each character is appended once, the runtime stays O(n + L), where n is the number of rules and L is the total length of all expanded strings.
Approach 2: Graph + Topological Sort (Time: O(n + L), Space: O(n + L))
You can model the substitutions explicitly as a dependency graph and resolve them in topological order. First build adjacency lists showing which keys depend on which other keys. Then compute in‑degrees and run a BFS‑style topological sort. Keys with no dependencies resolve immediately because their substitution strings contain only literals.
As each node is processed, replace references in dependent nodes with the resolved value and reduce their in‑degree. Once a dependent node has no remaining unresolved references, it becomes eligible for processing. This approach avoids recursion and works well when the dependency graph is large or when iterative solutions are preferred.
Recommended for interviews: The Hash Table + DFS approach is typically expected. It is concise, naturally expresses dependency resolution, and shows familiarity with memoization and graph traversal. Mentioning the topological sort alternative demonstrates deeper understanding of dependency graphs and how DFS and BFS can both solve the same problem.
We use a hash table d to store the substitution mapping, and then define a function dfs to recursively replace the placeholders in the string.
The execution logic of the function dfs is as follows:
i of the first placeholder in the string s. If not found, return s;j of the first placeholder in the string s. If not found, return s;d[key];In the main function, we call the dfs function, pass in the text string text, and return the result.
The time complexity is O(m + n times L), and the space complexity is O(m + n times L). Where m is the length of the substitution mapping, and n and L are the length of the text string and the average length of the placeholders, respectively.
Python
Java
C++
Go
TypeScript
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Hash Table + DFS Recursion | O(n + L) | O(n + L) | General case. Clean recursive solution with memoization to resolve dependencies. |
| Graph + Topological Sort (BFS) | O(n + L) | O(n + L) | Useful when avoiding recursion or when explicitly modeling dependency graphs. |
leetcode 3481 Apply Substitutions | string replace method, Kahn's alg for topo sorting • Code-Yao • 1,303 views views
Watch 1 more video solutions →Practice Apply Substitutions with our built-in code editor and test cases.
Practice on FleetCode