Lexicographically Smallest Generated String
HardPrompt
You are given two strings, str1 and str2, of lengths n and m, respectively.
A string word of length n + m - 1 is defined to be generated by str1 and str2 if it satisfies the following conditions for each index 0 <= i <= n - 1:
- If
str1[i] == 'T', the substring ofwordwith sizemstarting at indexiis equal tostr2, i.e.,word[i..(i + m - 1)] == str2. - If
str1[i] == 'F', the substring ofwordwith sizemstarting at indexiis not equal tostr2, i.e.,word[i..(i + m - 1)] != str2.
Return the lexicographically smallest possible string that can be generated by str1 and str2. If no string can be generated, return an empty string "".
Example 1:
Input: str1 = "TFTF", str2 = "ab"
Output: "ababa"
Explanation:
The table below represents the string "ababa"
| Index | T/F | Substring of length m |
|---|---|---|
| 0 | 'T' |
"ab" |
| 1 | 'F' |
"ba" |
| 2 | 'T' |
"ab" |
| 3 | 'F' |
"ba" |
The strings "ababa" and "ababb" can be generated by str1 and str2.
Return "ababa" since it is the lexicographically smaller string.
Example 2:
Input: str1 = "TFTF", str2 = "abc"
Output: ""
Explanation:
No string that satisfies the conditions can be generated.
Example 3:
Input: str1 = "F", str2 = "d"
Output: "a"
Constraints:
1 <= n == str1.length <= 1041 <= m == str2.length <= 500str1consists only of'T'or'F'.str2consists only of lowercase English characters.
Approaches
2 approaches with complexity analysis and trade-offs.
A naive approach to this problem is to try generating all possible strings of length n + m - 1 and checking if they satisfy the given conditions. The search space is 26^(n+m-1), which is enormous. We can use backtracking to explore this search space more intelligently.
Algorithm
- Define a recursive function
solve(k, word)which attempts to fill characters from indexk. - The base case is
k == n + m - 1. A fullwordhas been constructed. - In the base case, validate the generated
wordagainst allnconditions fromstr1. - If all conditions are met, this is a valid string. Since we explore lexicographically, this is the smallest one. Return it.
- If not, return a failure indicator.
- In the recursive step, for the current index
k, loop through characterscfrom 'a' to 'z'. - Set
word[k] = c. - Optionally, perform pruning by checking any newly completed constraints.
- Call
solve(k + 1, word). - If the recursive call returns a valid string, propagate it up. Otherwise, continue the loop to try the next character.
- If the loop finishes without finding a solution, return a failure indicator.
Walkthrough
We can define a recursive function, say generate(index, current_word), that tries to fill the word character by character from left to right (from index = 0 to n+m-2).
At each index, we iterate through all possible characters from 'a' to 'z'. For each character, we place it at current_word[index] and recurse for the next index index + 1.
The base case for the recursion is when index == n + m - 1. At this point, we have a complete candidate word. We then verify if this word satisfies all n conditions given by str1 and str2. If it does, we have found a valid generated string. Since we are exploring characters in lexicographical order ('a' through 'z'), the first valid string we find will be the lexicographically smallest one.
To optimize, we can add pruning to the backtracking. After placing a character at current_word[index], we can check any conditions that have just become fully determined. For example, if index is i + m - 1, the substring word[i : i+m] is now complete. We can check the condition for str1[i] immediately. If it's violated, we can prune this entire branch of the search tree, avoiding unnecessary recursive calls.
Despite pruning, the worst-case time complexity remains exponential and is too slow for the given constraints.
Complexity
Time
O(26^(n+m-1) * n * m) in the worst case without pruning. Pruning helps but the complexity remains exponential. This is infeasible.
Space
O(n+m) for the recursion stack depth and to store the candidate string.
Trade-offs
Pros
Conceptually simple to understand.
Cons
Extremely inefficient, with a worst-case time complexity that is exponential in the length of the generated string.
Will not pass the time limits for the given constraints.
A much more efficient approach is a greedy one. We aim to construct the lexicographically smallest string. This means we should try to use the character 'a' as much as possible, and for other characters, use the smallest possible ones. The strategy involves three phases: first, satisfy all 'T' constraints; second, greedily fill the rest of the string with 'a'; third, verify and fix any violations of 'F' constraints.
Algorithm
- Initialize a character array
wordof lengthn + m - 1and a boolean arrayis_fixedof the same length. - Phase 1: Iterate
ifrom0ton-1. Ifstr1[i] == 'T':- For each
jfrom0tom-1, letk = i + j. - If
is_fixed[k]is true andword[k]is notstr2[j], return""(conflict). - Set
word[k] = str2[j]andis_fixed[k] = true.
- For each
- Phase 2: Iterate
kfrom0ton+m-2. If!is_fixed[k], setword[k] = 'a'. - Phase 3: Iterate
ifrom0ton-1. Ifstr1[i] == 'F':- Check if the substring
word[i : i+m]equalsstr2. - If they are equal, we must fix it:
- Iterate
kfromi+m-1down toi. - If
!is_fixed[k]andword[k] < 'z':- Increment
word[k]. Break the inner loop (overk).
- Increment
- If the inner loop completes without finding a character to change, return
""(impossible to fix).
- Iterate
- Check if the substring
- Finally, convert the
wordarray to a string and return it.
Walkthrough
The algorithm works as follows:
-
Phase 1: Handle 'T' Constraints We create a character array
wordof sizen + m - 1and a boolean arrayis_fixedof the same size. We iterate throughstr1. Whenever we findstr1[i] == 'T', we must placestr2intowordat the interval[i, i + m - 1]. We mark these positions inis_fixedastrue. If we encounter a conflict (i.e., a position is already fixed to a different character), it's impossible to generate a valid string, so we return"". -
Phase 2: Greedy Fill After Phase 1, some positions in
wordare fixed. To make the string lexicographically smallest, we should fill all non-fixed positions with the smallest possible character, which is 'a'. -
Phase 3: Verify and Fix 'F' Constraints The
wordwe have now is the lexicographically smallest string that satisfies all 'T' constraints. However, it might violate some 'F' constraints. We iterate throughstr1fromi = 0ton-1. Ifstr1[i] == 'F', we check ifword.substring(i, i + m)is equal tostr2. If it is, we have a violation. We must modifywordto break this equality. To maintain the lexicographically smallest result, we should make the smallest possible change at the rightmost possible position. We scan the substringword[i : i+m]from right to left (from indexi+m-1down toi). We look for the first characterword[k]that is not fixed (i.e.,is_fixed[k]is false) and is not 'z'. If we find such a character, we increment it (word[k]++). This change guarantees that the substring is no longer equal tostr2and has the minimal lexicographical impact. If we cannot find any such character to modify in the window (e.g., all are fixed or are 'z'), then it's impossible to satisfy this 'F' constraint, and we return"".
This process ensures that we find the lexicographically smallest valid string because we start with the smallest possible candidate and make minimal necessary changes to satisfy all conditions.
Complexity
Time
O(n * m). Phase 1 takes O(n * m) to fill 'T' constraints. Phase 2 is O(n+m). Phase 3 involves a loop of size `n`, and inside it, a substring comparison of length `m` and a potential fixup loop of length `m`, leading to O(n * m). The total complexity is dominated by O(n * m).
Space
O(n + m) to store the `word` character array and the `is_fixed` boolean array.
Trade-offs
Pros
Efficient, with a polynomial time complexity.
Correctly finds the lexicographically smallest string by making greedy choices and minimal modifications.
Relatively straightforward to implement.
Cons
The logic requires careful handling of array indices and constraints.
Involves multiple passes over the data structures.
Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.