Hard

题目描述

给定两个字符串 str1str2,长度分别为 nm

如果一个长度为 n + m - 1 的字符串 word 对于每个索引 0 <= i <= n - 1 都满足以下条件,则称它是由 str1str2 生成的:

  • 如果 str1[i] == 'T',则从索引 i 开始长度为 m 的子串等于 str2,即 word[i..(i + m - 1)] == str2
  • 如果 str1[i] == 'F',则从索引 i 开始长度为 m 的子串不等于 str2,即 word[i..(i + m - 1)] != str2

返回可以由 str1str2 生成的字典序最小的字符串。如果无法生成任何字符串,返回空字符串 ""

示例 1:

输入:str1 = "TFTF", str2 = "ab"
输出:"ababa"

解释:
下表表示字符串 "ababa"

索引    T/F    长度为 m 的子串
0       'T'    "ab"
1       'F'    "ba"
2       'T'    "ab"
3       'F'    "ba"

字符串 "ababa" 和 "ababb" 都可以由 str1 和 str2 生成。
返回 "ababa",因为它字典序更小。

示例 2:

输入:str1 = "TFTF", str2 = "abc"
输出:""

解释:
无法生成满足条件的字符串。

示例 3:

输入:str1 = "F", str2 = "d"
输出:"a"

约束条件:

  • 1 <= n == str1.length <= 10^4
  • 1 <= m == str2.length <= 500
  • str1 仅包含 ‘T’ 或 ‘F’
  • str2 仅包含小写英文字母

解题思路

这道题需要构造一个字符串,使其满足模式匹配的约束条件,并且字典序最小。

核心思路:

  1. 使用动态规划 + KMP算法的思想
  2. 状态定义:dp[i][j] 表示已经确定了前 i 个字符,且当前后缀与 str2 的前缀匹配长度为 j 的情况下,是否可能构造出有效解
  3. 对于每个位置,我们贪心地选择字典序最小的字符

关键观察:

  • str1[i] == 'T' 时,从位置 i 开始的长度为 m 的子串必须等于 str2
  • str1[i] == 'F' 时,从位置 i 开始的长度为 m 的子串必须不等于 str2
  • 我们需要使用KMP的 failure function 来高效地处理字符串匹配

算法步骤:

  1. 预处理 str2 的 failure function
  2. 使用动态规划,状态为 (位置, 当前匹配长度)
  3. 对每个位置贪心选择最小的可行字符
  4. 检查所有约束是否满足

由于字符集只有小写字母,我们可以从 ‘a’ 开始尝试每个字符,选择第一个满足条件的。

代码实现

class Solution {
public:
    string generateString(string str1, string str2) {
        int n = str1.size(), m = str2.size();
        
        // KMP failure function
        vector<int> lps(m, 0);
        for (int i = 1, j = 0; i < m; i++) {
            while (j > 0 && str2[i] != str2[j]) j = lps[j - 1];
            if (str2[i] == str2[j]) j++;
            lps[i] = j;
        }
        
        // DP state: dp[pos][match_len] = possible
        vector<vector<bool>> dp(n + m, vector<bool>(m + 1, false));
        dp[0][0] = true;
        
        string result(n + m - 1, 'a');
        
        for (int pos = 0; pos < n + m - 1; pos++) {
            for (char c = 'a'; c <= 'z'; c++) {
                bool valid = false;
                
                for (int prevMatch = 0; prevMatch <= m && prevMatch <= pos; prevMatch++) {
                    if (!dp[pos][prevMatch]) continue;
                    
                    int newMatch = prevMatch;
                    while (newMatch > 0 && str2[newMatch] != c) {
                        newMatch = lps[newMatch - 1];
                    }
                    if (str2[newMatch] == c) newMatch++;
                    
                    // Check constraints
                    bool canUse = true;
                    for (int i = 0; i < n; i++) {
                        if (pos - i >= 0 && pos - i < m) {
                            if (str1[i] == 'T') {
                                if (pos - i == m - 1) {
                                    canUse &= (newMatch == m);
                                }
                            } else {
                                if (pos - i == m - 1) {
                                    canUse &= (newMatch != m);
                                }
                            }
                        }
                    }
                    
                    if (canUse) {
                        dp[pos + 1][newMatch] = true;
                        valid = true;
                    }
                }
                
                if (valid) {
                    result[pos] = c;
                    break;
                }
            }
            
            if (result[pos] == 'a') {
                bool hasValidChar = false;
                for (char c = 'a'; c <= 'z'; c++) {
                    bool valid = false;
                    for (int prevMatch = 0; prevMatch <= m && prevMatch <= pos; prevMatch++) {
                        if (!dp[pos][prevMatch]) continue;
                        valid = true;
                        break;
                    }
                    if (valid) {
                        hasValidChar = true;
                        break;
                    }
                }
                if (!hasValidChar) return "";
            }
        }
        
        return result;
    }
};
class Solution:
    def generateString(self, str1: str, str2: str) -> str:
        n, m = len(str1), len(str2)
        
        # KMP failure function
        lps = [0] * m
        j = 0
        for i in range(1, m):
            while j > 0 and str2[i] != str2[j]:
                j = lps[j - 1]
            if str2[i] == str2[j]:
                j += 1
            lps[i] = j
        
        # DP with memoization
        from functools import lru_cache
        
        @lru_cache(None)
        def canGenerate(pos, match_len):
            if pos == n + m - 1:
                return True
            
            for c in 'abcdefghijklmnopqrstuvwxyz':
                new_match = match_len
                while new_match > 0 and str2[new_match] != c:
                    new_match = lps[new_match - 1]
                if str2[new_match] == c:
                    new_match += 1
                
                valid = True
                # Check constraints
                for i in range(n):
                    window_end = pos - i
                    if 0 <= window_end < m:
                        if str1[i] == 'T' and window_end == m - 1:
                            valid &= (new_match == m)
                        elif str1[i] == 'F' and window_end == m - 1:
                            valid &= (new_match != m)
                
                if valid and canGenerate(pos + 1, new_match):
                    return True
            return False
        
        if not canGenerate(0, 0):
            return ""
        
        result = []
        match_len = 0
        
        for pos in range(n + m - 1):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                new_match = match_len
                while new_match > 0 and str2[new_match] != c:
                    new_match = lps[new_match - 1]
                if str2[new_match] == c:
                    new_match += 1
                
                valid = True
                for i in range(n):
                    window_end = pos - i
                    if 0 <= window_end < m:
                        if str1[i] == 'T' and window_end == m - 1:
                            valid &= (new_match == m)
                        elif str1[i] == 'F' and window_end == m - 1:
                            valid &= (new_match != m)
                
                if valid and canGenerate(pos + 1, new_match):
                    result.append(c)
                    match_len = new_match
                    break
        
        return ''.join(result)
public class Solution {
    public string GenerateString(string str1, string str2) {
        int n = str1.Length, m = str2.Length;
        
        // KMP failure function
        int[] lps = new int[m];
        for (int i = 1, j = 0; i < m; i++) {
            while (j > 0 && str2[i] != str2[j]) j = lps[j - 1];
            if (str2[i] == str2[j]) j++;
            lps[i] = j;
        }
        
        // Check if solution exists
        var memo = new Dictionary<(int, int), bool>();
        
        bool CanGenerate(int pos, int matchLen) {
            if (pos == n + m - 1) return true;
            if (memo.ContainsKey((pos, matchLen))) return memo[(pos, matchLen)];
            
            for (char c = 'a'; c <= 'z'; c++) {
                int newMatch = matchLen;
                while (newMatch > 0 && str2[newMatch] != c) {
                    newMatch = lps[newMatch - 1];
                }
                if (str2[newMatch] == c) newMatch++;
                
                bool valid = true;
                for (int i = 0; i < n; i++) {
                    int windowEnd = pos - i;
                    if (windowEnd >= 0 && windowEnd < m) {
                        if (str1[i] == 'T' && windowEnd == m - 1) {
                            valid &= (newMatch == m);
                        } else if (str1[i] == 'F' && windowEnd == m - 1) {
                            valid &= (newMatch != m);
                        }
                    }
                }
                
                if (valid && CanGenerate(pos + 1, newMatch)) {
                    memo[(pos, matchLen)] = true;
                    return true;
                }
            }
            memo[(pos, matchLen)] = false;
            return false;
        }
        
        if (!CanGenerate(0, 0)) return "";
        
        var result = new char[n + m - 1];
        int currentMatch = 0;
        
        for (int pos = 0; pos < n + m - 1; pos++) {
            for (char c = 'a'; c <= 'z'; c++) {
                int newMatch = currentMatch;
                while (newMatch > 0 && str2[newMatch] != c) {
                    newMatch = lps[newMatch - 1];
                }
                if (str2[newMatch] == c) newMatch++;
                
                bool valid = true;
                for (int i = 0; i < n; i++) {
                    int windowEnd = pos - i;
                    if (windowEnd >= 0 && windowEnd < m) {
                        if (str1[i] == 'T' && windowEnd == m - 1) {
                            valid &= (newMatch == m);
                        } else if (str1[i] == 'F' && windowEnd == m - 1) {
                            valid &= (newMatch != m);
                        }
                    }
                }
                
                if (valid && CanGenerate(pos + 1, newMatch)) {
                    result[pos] = c;
                    currentMatch = newMatch;
                    break;
                }
            }
        }
        
        return new string(result);
    }
}
/**
 * @param {string} str1
 * @param {string} str2
 * @return {string}
 */
var generateString = function(str1, str2) {
    const n = str1.length, m = str2.length;
    
    // KMP failure function
    const lps = new Array(m).fill(0);
    for (let i = 1, j = 0; i < m; i++) {
        while (j > 0 && str2[i] !== str2[j]) j = lps[j - 1];
        if (str2[i]

复杂度分析

项目复杂度
时间复杂度O(26 × (n + m) × m × n)
空间复杂度O((n + m) × m)

其中 n 是 str1 的长度,m 是 str2 的长度。时间复杂度包括:

  • 对每个位置(n+m-1个)尝试26个字符
  • 每次尝试需要检查所有约束条件(最多n个)
  • KMP匹配更新需要O(m)时间 空间复杂度主要用于动态规划的记忆化存储。

相关题目