每日一题 力扣 3474. 字典序最小的生成字符串 贪心 字符串 C++ 题解

文章目录

题目描述

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

示例 1:

输入:str1 = "TFFT", str2 = "abc"

输出:"abcba"

解释:

"abcba" 是满足条件的字典序最小的字符串。

str1[0] == 'T' :word[0...2] = "abc" ,等于 str2 。

str1[1] == 'F' :word[1...3] = "bcb" ,不等于 str2 。

str1[2] == 'F' :word[2...4] = "cba" ,不等于 str2 。

str1[3] == 'T' :word[3...5] 不存在(因为 word 长度为 5),所以无需考虑。
示例 2:

输入:str1 = "T", str2 = "a"

输出:"a"

解释:

word 长度为 1 + 1 - 1 = 1 ,且 str1[0] == 'T' ,所以 word[0] 必须等于 str2[0] ,即 "a" 。
示例 3:

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

输出:"b"

解释:

word 长度为 1 ,且 str1[0] == 'F' ,所以 word[0] 不能等于 "a" 。字典序最小的字符是 "b" 。
提示:

1 <= n, m <= 105

1 <= n + m - 1 <= 105

str1 只包含字符 'T' 和 'F' 。

str2 只包含小写英文字母。

思路简述

本题的核心目标是生成字典序最小 的字符串,同时满足 "T" 和 "F" 的约束。整体策略分为两个部分:先处理 "T" 的强制约束 ,再处理 "F" 的禁止约束,并在每一步中都优先保证字典序最小。

第一步:处理 "T" 约束(固定必须相等的位置)

"T" 约束是硬性要求:如果 str1[i] == 'T',那么从 i 开始的子串必须完全等于 str2

  • 我们先初始化结果字符串 word 为全 'a'(字典序最小的默认值),并创建一个 fixed 数组标记哪些位置被 "T" 约束固定死了(后续不能随意修改)。
  • 遍历所有 "T" 的位置,将对应子串的字符强制赋值为 str2 的字符。如果在赋值过程中发现某个位置已经被之前的 "T" 约束固定为不同的字符,说明冲突,直接返回空字符串。

第二步:处理 "F" 约束(避免子串等于 str2)

"F" 约束要求对应子串不能 等于 str2。此时我们已经用 "T" 约束固定了部分位置,剩下的位置默认是 'a'。

  • 遍历所有 "F" 的位置,检查当前窗口内的子串是否已经不等于 str2:如果是,直接跳过,如果不幸完全等于 str2,我们需要修改一个字符来打破这个相等关系。
  • 为了保证字典序最小,我们应该尽可能修改窗口最右侧的非固定字符(将 'a' 改为 'b')。因为越靠右的字符对字典序的影响越小,且 'b' 是比 'a' 大的最小字符。

关键证明:修改最右侧字符不会违反之前的 "F" 约束

这时我们可能会担心:修改当前窗口的字符后,会不会导致前面某个已经处理好的 "F" 窗口又变成等于 str2 了?

这里可以通过反证法证明这种情况不可能发生

假设存在两个 "F" 位置 i1i2i1 < i2),处理 i2 时修改了某个字符,导致 i1 窗口又等于 str2。通过分析字符串的前后缀关系、字符的固定性,最终会推导出 "a" 必须等于 "b" 的矛盾结论。因此,每次修改最右侧的非固定字符是安全的,不会破坏之前的约束。

代码实现

cpp 复制代码
class Solution {
public:
    string generateString(string str1, string str2) {
        int n = str1.size(), m = str2.size();
        int len = n + m - 1; // 结果字符串的长度
        string s(len, 'a');  // 初始化为全 'a',保证字典序最小
        vector<int> fixed(len, 0); // 标记哪些位置被 'T' 约束固定(1=固定,0=可修改)

        // 第一步:处理所有 'T' 约束
        for (int i = 0; i < n; i++) {
            if (str1[i] == 'T') {
                // 遍历当前 'T' 对应的子串窗口 [i, i+m-1]
                for (int j = i; j < i + m; j++) {
                    // 如果该位置已被固定,且与当前要填的字符冲突 → 无解
                    if (fixed[j] && s[j] != str2[j - i]) {
                        return "";
                    } else {
                        // 填充字符并标记为固定
                        s[j] = str2[j - i];
                        fixed[j] = 1;
                    }
                }
            }
        }

        // 第二步:处理所有 'F' 约束
        for (int i = 0; i < n; i++) {
            if (str1[i] == 'F') {
                bool flag = false; // 标记当前窗口是否已经不等于 str2
                int idx = -1;       // 记录窗口内最右侧的可修改位置

                // 从右往左遍历窗口 [i, i+m-1]
                for (int j = i + m - 1; j >= i; j--) {
                    // 只要有一个字符不等于 str2,说明当前窗口已经满足 'F' 约束
                    if (str2[j - i] != s[j]) {
                        flag = true;
                    }
                    // 同时记录最右侧的非固定位置(如果还没找到的话)
                    if (idx == -1 && !fixed[j]) {
                        idx = j;
                    }
                }

                if (flag) {
                    // 情况1:窗口已经不等于 str2,直接跳过
                    continue;
                } else if (idx != -1) {
                    // 情况2:窗口等于 str2,但有可修改的位置 → 修改最右侧的 'a' 为 'b'
                    s[idx] = 'b';
                } else {
                    // 情况3:窗口等于 str2,且所有位置都被固定 → 无解
                    return "";
                }
            }
        }

        return s;
    }
};

复杂度分析

  • 时间复杂度 : O ( n × m ) O(n \times m) O(n×m)
    处理 "T" 和 "F" 约束时,每个约束都需要遍历长度为 m m m 的窗口。在最坏情况下(如 str1 全是 'T' 或 'F'),总操作数与 n × m n \times m n×m 成正比。
  • 空间复杂度 : O ( n + m ) O(n + m) O(n+m)
    主要开销为结果字符串 s 和标记数组 fixed,长度均为 n + m − 1 n + m - 1 n+m−1,与输入规模线性相关。

踩坑记录

  1. 顺序不能乱:必须先处理 "T" 约束再处理 "F" 约束。如果先填了 'a' 再处理 "T",可能会覆盖掉必要的固定字符,导致逻辑错误。
  2. 字典序的细节 :处理 "F" 时,一定要从右往左找可修改位置。如果从左往右改,会把左边的 'a' 改成 'b',导致字典序变大(比如 "ab" 比 "ba" 小)。
  3. 冲突检测:处理 "T" 时,不仅要填充字符,还要检查是否和已固定的字符冲突。如果两个 "T" 的窗口重叠但要求的字符不一样,直接返回空字符串。

如果这篇博客对你有帮助,别忘了点赞支持一下~也可以收藏起来,方便后续刷题复习时随时翻看。要是能顺手点个关注,爱弥斯还能得到漂泊者批准的游戏时间哦!

相关推荐
人道领域2 小时前
LeetCode【刷题日记】:螺旋矩阵逆向全过程,边界缩进优化
算法·leetcode·矩阵
ALex_zry2 小时前
物联网数据质量控制系统设计:质控算法与实现
物联网·算法·struts
EQUINOX12 小时前
货物运输问题,前缀和优化dp,[牛客周赛137 F-小苯的糖果盒]
算法·动态规划
小此方2 小时前
Re:从零开始的 C++ STL篇(九)AVL树太“较真”,红黑树更“现实”:一文讲透工程中的平衡之道
开发语言·数据结构·c++·算法·stl
地平线开发者2 小时前
多 Batch 量化校准与单 Batch 校准的数值差异
算法·自动驾驶
少许极端2 小时前
算法奇妙屋(三十八)-贪心算法学习之路 5
java·学习·算法·贪心算法
im_AMBER2 小时前
Leetcode 150 最小路径和 | 最长回文子串
数据结构·算法·leetcode
进击的荆棘2 小时前
C++起始之路——二叉搜索树
数据结构·c++·stl
少司府2 小时前
C++基础入门:类和对象(上)
c语言·开发语言·c++·类和对象·访问限定符