Using greedy without proof
Wrong move: Locally optimal choices may fail globally.
Usually fails on: Counterexamples appear on crafted input orderings.
Fix: Verify with exchange argument or monotonic objective before committing.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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:
str1[i] == 'T', the substring of word with size m starting at index i is equal to str2, i.e., word[i..(i + m - 1)] == str2.str1[i] == 'F', the substring of word with size m starting at index i is not equal to str2, 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:
"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 <= 500str1 consists only of 'T' or 'F'.str2 consists only of lowercase English characters.Problem summary: 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 of word with size m starting at index i is equal to str2, i.e., word[i..(i + m - 1)] == str2. If str1[i] == 'F', the substring of word with size m starting at index i is not equal to str2, 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 "".
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Greedy · String Matching
"TFTF" "ab"
"TFTF" "abc"
"F" "d"
lexicographically-smallest-equivalent-string)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
class Solution {
public String generateString(String str1, String str2) {
final int n = str1.length();
final int m = str2.length();
final int sz = n + m - 1;
char[] ans = new char[sz];
boolean[] modifiable = new boolean[sz];
Arrays.fill(modifiable, true);
// 1. Handle all 'T' positions first.
for (int i = 0; i < n; ++i)
if (str1.charAt(i) == 'T')
for (int j = 0; j < m; ++j) {
final int pos = i + j;
if (ans[pos] != 0 && ans[pos] != str2.charAt(j))
return "";
ans[pos] = str2.charAt(j);
modifiable[pos] = false;
}
// 2. Fill all remaining positions with 'a'.
for (int i = 0; i < sz; ++i)
if (ans[i] == 0)
ans[i] = 'a';
// 3. Handle all 'F' positions.
for (int i = 0; i < n; ++i)
if (str1.charAt(i) == 'F' && match(ans, i, str2)) {
final int modifiablePos = lastModifiablePosition(i, m, modifiable);
if (modifiablePos == -1)
return "";
ans[modifiablePos] = 'b';
modifiable[modifiablePos] = false;
}
return new String(ans);
}
// Returns true if the substring of ans starting at `i` matches `s`.
private boolean match(char[] ans, int i, String s) {
for (int j = 0; j < s.length(); ++j)
if (ans[i + j] != s.charAt(j))
return false;
return true;
}
// Finds the last modifiable position in the substring ans starting at `i`.
private int lastModifiablePosition(int i, int m, boolean[] modifiable) {
int modifiablePos = -1;
for (int j = 0; j < m; ++j) {
final int pos = i + j;
if (modifiable[pos])
modifiablePos = pos;
}
return modifiablePos;
}
}
// Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
package main
import (
"bytes"
)
// https://space.bilibili.com/206214
func calcZ(s string) []int {
n := len(s)
z := make([]int, n)
boxL, boxR := 0, 0 // z-box 左右边界(闭区间)
for i := 1; i < n; i++ {
if i <= boxR {
z[i] = min(z[i-boxL], boxR-i+1)
}
for i+z[i] < n && s[z[i]] == s[i+z[i]] {
boxL, boxR = i, i+z[i]
z[i]++
}
}
z[0] = n
return z
}
func generateString(s, t string) string {
n, m := len(s), len(t)
ans := bytes.Repeat([]byte{'?'}, n+m-1)
// 处理 T
pre := -m
z := calcZ(t)
for i, b := range s {
if b != 'T' {
continue
}
size := max(pre+m-i, 0)
// t 的长为 size 的前后缀必须相同
if size > 0 && z[m-size] < size {
return ""
}
// size 后的内容都是 '?',填入 t
copy(ans[i+size:], t[size:])
pre = i
}
// 计算 <= i 的最近待定位置
preQ := make([]int, len(ans))
pre = -1
for i, c := range ans {
if c == '?' {
ans[i] = 'a' // 待定位置的初始值为 a
pre = i
}
preQ[i] = pre
}
// 找 ans 中的等于 t 的位置,可以用 KMP 或者 Z 函数
z = calcZ(t + string(ans))
// 处理 F
for i := 0; i < n; i++ {
if s[i] != 'F' {
continue
}
// 子串必须不等于 t
if z[m+i] < m {
continue
}
// 找最后一个待定位置
j := preQ[i+m-1]
if j < i { // 没有
return ""
}
ans[j] = 'b'
i = j // 直接跳到 j
}
return string(ans)
}
# Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
class Solution:
def generateString(self, str1: str, str2: str) -> str:
n = len(str1)
m = len(str2)
sz = n + m - 1
ans = [None] * sz
modifiable = [True] * sz
# 1. Handle all 'T' positions first.
for i, tf in enumerate(str1):
if tf == 'T':
for j, c in enumerate(str2):
pos = i + j
if ans[pos] and ans[pos] != c:
return ''
ans[pos] = c
modifiable[pos] = False
# 2. Fill all remaining positions with 'a'.
for i in range(sz):
if not ans[i]:
ans[i] = 'a'
# 3. Handle all 'F' positions.
for i in range(n):
if str1[i] == 'F' and self._match(ans, i, str2):
modifiablePos = self._lastModifiablePosition(i, m, modifiable)
if modifiablePos == -1:
return ''
ans[modifiablePos] = 'b'
modifiable[modifiablePos] = False
return ''.join(ans)
def _match(self, ans: list, i: int, s: str) -> bool:
"""Returns True if the substring of ans starting at `i` matches `s`."""
for j, c in enumerate(s):
if ans[i + j] != c:
return False
return True
def _lastModifiablePosition(self, i: int, m: int, modifiable: list) -> int:
"""
Finds the last modifiable position in the substring of ans starting at `i`.
"""
modifiablePos = -1
for j in range(m):
pos = i + j
if modifiable[pos]:
modifiablePos = pos
return modifiablePos
// Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
// class Solution {
// public String generateString(String str1, String str2) {
// final int n = str1.length();
// final int m = str2.length();
// final int sz = n + m - 1;
// char[] ans = new char[sz];
// boolean[] modifiable = new boolean[sz];
// Arrays.fill(modifiable, true);
//
// // 1. Handle all 'T' positions first.
// for (int i = 0; i < n; ++i)
// if (str1.charAt(i) == 'T')
// for (int j = 0; j < m; ++j) {
// final int pos = i + j;
// if (ans[pos] != 0 && ans[pos] != str2.charAt(j))
// return "";
// ans[pos] = str2.charAt(j);
// modifiable[pos] = false;
// }
//
// // 2. Fill all remaining positions with 'a'.
// for (int i = 0; i < sz; ++i)
// if (ans[i] == 0)
// ans[i] = 'a';
//
// // 3. Handle all 'F' positions.
// for (int i = 0; i < n; ++i)
// if (str1.charAt(i) == 'F' && match(ans, i, str2)) {
// final int modifiablePos = lastModifiablePosition(i, m, modifiable);
// if (modifiablePos == -1)
// return "";
// ans[modifiablePos] = 'b';
// modifiable[modifiablePos] = false;
// }
//
// return new String(ans);
// }
//
// // Returns true if the substring of ans starting at `i` matches `s`.
// private boolean match(char[] ans, int i, String s) {
// for (int j = 0; j < s.length(); ++j)
// if (ans[i + j] != s.charAt(j))
// return false;
// return true;
// }
//
// // Finds the last modifiable position in the substring ans starting at `i`.
// private int lastModifiablePosition(int i, int m, boolean[] modifiable) {
// int modifiablePos = -1;
// for (int j = 0; j < m; ++j) {
// final int pos = i + j;
// if (modifiable[pos])
// modifiablePos = pos;
// }
// return modifiablePos;
// }
// }
// Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #3474: Lexicographically Smallest Generated String
// class Solution {
// public String generateString(String str1, String str2) {
// final int n = str1.length();
// final int m = str2.length();
// final int sz = n + m - 1;
// char[] ans = new char[sz];
// boolean[] modifiable = new boolean[sz];
// Arrays.fill(modifiable, true);
//
// // 1. Handle all 'T' positions first.
// for (int i = 0; i < n; ++i)
// if (str1.charAt(i) == 'T')
// for (int j = 0; j < m; ++j) {
// final int pos = i + j;
// if (ans[pos] != 0 && ans[pos] != str2.charAt(j))
// return "";
// ans[pos] = str2.charAt(j);
// modifiable[pos] = false;
// }
//
// // 2. Fill all remaining positions with 'a'.
// for (int i = 0; i < sz; ++i)
// if (ans[i] == 0)
// ans[i] = 'a';
//
// // 3. Handle all 'F' positions.
// for (int i = 0; i < n; ++i)
// if (str1.charAt(i) == 'F' && match(ans, i, str2)) {
// final int modifiablePos = lastModifiablePosition(i, m, modifiable);
// if (modifiablePos == -1)
// return "";
// ans[modifiablePos] = 'b';
// modifiable[modifiablePos] = false;
// }
//
// return new String(ans);
// }
//
// // Returns true if the substring of ans starting at `i` matches `s`.
// private boolean match(char[] ans, int i, String s) {
// for (int j = 0; j < s.length(); ++j)
// if (ans[i + j] != s.charAt(j))
// return false;
// return true;
// }
//
// // Finds the last modifiable position in the substring ans starting at `i`.
// private int lastModifiablePosition(int i, int m, boolean[] modifiable) {
// int modifiablePos = -1;
// for (int j = 0; j < m; ++j) {
// final int pos = i + j;
// if (modifiable[pos])
// modifiablePos = pos;
// }
// return modifiablePos;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Try every possible combination of choices. With n items each having two states (include/exclude), the search space is 2ⁿ. Evaluating each combination takes O(n), giving O(n × 2ⁿ). The recursion stack or subset storage uses O(n) space.
Greedy algorithms typically sort the input (O(n log n)) then make a single pass (O(n)). The sort dominates. If the input is already sorted or the greedy choice can be computed without sorting, time drops to O(n). Proving greedy correctness (exchange argument) is harder than the implementation.
Review these before coding to avoid predictable interview regressions.
Wrong move: Locally optimal choices may fail globally.
Usually fails on: Counterexamples appear on crafted input orderings.
Fix: Verify with exchange argument or monotonic objective before committing.