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

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

题目描述

给你两个字符串,str1 和 str2,其长度分别为 n 和 m 。

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

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

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

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

如果字符串 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)

    存储 ans 和 old_ans 字符串。

总结

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

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

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

相关推荐
达子66619 分钟前
RUST 图解 第 3 章:通用编程概念(Common Programming Concepts)
开发语言·算法·rust
夏幻灵40 分钟前
每日一题:LeetCode 165:比较版本号
算法·leetcode·职场和发展
道尔柯南41 分钟前
C++ 面向对象编程入门:类和对象(上)
开发语言·c++·算法
Navigator_Z1 小时前
LeetCode //C - 1288. Remove Covered Intervals
c语言·算法·leetcode
码爸1 小时前
决策树介绍与示例
python·算法·机器学习
动词ing2 小时前
【题目练习】字符串相乘
算法
0+1112 小时前
算法 --链表
数据结构·算法·leetcode·链表
圣保罗的大教堂2 小时前
leetcode 2333. 最小差值平方和 中等
leetcode
aqiu1111112 小时前
【C++算法打怪专栏】C++ `unordered_set` 常用初始化与插入方法总结
开发语言·c++·算法
Terra.K2 小时前
高精度阶乘求和模板深度解析
开发语言·c++·算法