LeetCode 3474. 字典序最小的生成字符串

LeetCode 3474. 字典序最小的生成字符串

题目描述

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

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

如果 str1[i] == 'T',则长度为 m 的 子字符串(从下标 i 开始)与 str2 相等,即 word[i..(i + m - 1)] == str2

如果 str1[i] == 'F',则长度为 m 的 子字符串(从下标 i 开始)与 str2 不相等,即 word[i..(i + m - 1)] != str2

返回可以由 str1str2 生成 的 字典序最小 的字符串。如果不存在满足条件的字符串,返回空字符串 ""

如果字符串 a 在第一个不同字符的位置上比字符串 b 的对应字符在字母表中更靠前,则称字符串 a 的 字典序 小于 字符串 b

如果前 min(a.length, b.length) 个字符都相同,则较短的字符串字典序更小。

子字符串 是字符串中的一个连续、非空的字符序列。

思路分析

这道题属于字符串构造问题,关键在于同时满足多组"必须相等"和"必须不等"的约束。我们可以分两步解决:

  1. 强制满足所有 'T' 条件

    对于每个 s[i] == 'T',将 ans[i..i+m-1] 直接赋值为 t。如果不同位置要求冲突(比如同一个位置既要填 t[j] 又要填不同的字符),则直接无解。

  2. 处理所有 'F' 条件

    'T' 已经确定的基础上,先将未确定的位置(仍为 '?')填充为默认字符 'a',得到一个候选字符串。

    然后检查每个 'F' 位置:如果当前子串恰好等于 t,就说明违反了条件,需要破坏它。

    破坏的方法:从子串的末尾向前找到第一个原本是 '?' 的位置(即未被 'T' 强制固定的位置),将其改为 'b',这样该子串就不再等于 t

    如果找不到这样的位置(子串所有位置都被 'T' 锁死),则无解。

为什么从后向前找?因为后面的子串起始索引更大,修改靠后的位置对后续子串的影响最小,可以避免不必要的冲突。

代码实现

cpp 复制代码
class Solution {
public:
    string generateString(string s, string t) {
        int n = s.size(), m = t.size();
        string ans(n + m - 1, '?');   // 初始化全为 '?'

        // 步骤1:处理所有 'T' 条件
        for (int i = 0; i < n; ++i) {
            if (s[i] != 'T') continue;
            for (int j = 0; j < m; ++j) {
                char v = ans[i + j];
                if (v != '?' && v != t[j]) return ""; // 冲突
                ans[i + j] = t[j];
            }
        }

        string old_ans = ans;          // 记录哪些位置是原本由 'T' 确定的
        // 步骤2:默认填充 'a'
        for (char& c : ans) {
            if (c == '?') c = 'a';
        }

        // 步骤3:处理所有 'F' 条件
        for (int i = 0; i < n; ++i) {
            if (s[i] != 'F') continue;
            // 检查当前子串是否等于 t
            if (string(ans.begin() + i, ans.begin() + i + m) != t)
                continue;             // 已经满足,跳过

            bool same = false;
            // 从后向前找第一个原本为 '?' 的位置
            for (int j = i + m - 1; j >= i; --j) {
                if (old_ans[j] == '?') {
                    ans[j] = 'b';     // 改为 'b' 破坏匹配
                    same = true;
                    break;
                }
            }
            if (!same) return "";     // 无法破坏,无解
        }

        return ans;
    }
};

复杂度分析

  • 时间复杂度 :O(n·m)

    最坏情况下,每个 'T''F' 位置都需要遍历长度为 m 的子串。实际常数较小。

  • 空间复杂度 :O(n + m)

    存储 ansold_ans 字符串。

总结

本题通过先满足所有强制相等条件,再破坏所有强制不等条件的策略,利用贪心方法逐个解决冲突。关键在于:

  • 'T' 要求的子串直接填充,同时检测冲突。
  • 'F' 条件,仅当当前子串意外相等时才进行破坏,且破坏时优先选择原本可修改的位置。
  • 从后向前修改可以减少对其他条件的干扰。

这种方法简单直观,且能有效判断无解情况。

相关推荐
REDcker2 小时前
Nagle 算法与 TCP_NODELAY、TCP_CORK 详解
网络·tcp/ip·算法
AlenTech2 小时前
136. 只出现一次的数字 - 力扣(LeetCode)
leetcode
β添砖java2 小时前
深度优先搜索DFS
算法·深度优先
小糯米6012 小时前
C++ 并查集
java·c++·算法
IronMurphy2 小时前
【算法三十四】39. 组合总和
算法·深度优先
重庆小透明2 小时前
力扣刷题【3】相交链表
算法·leetcode·链表
算法鑫探2 小时前
C语言实战:学生成绩统计与分析
c语言·数据结构·算法·新人首发
IAUTOMOBILE2 小时前
Code Marathon 项目源码解析与技术实践
java·前端·算法
Lyyaoo.2 小时前
【JAVA基础面经】深拷贝与浅拷贝
java·开发语言·算法