《LeetCode 978 最长湍流子数组 || LeetCode 139 单词拆分》

一、题目

二、做题思路

2.1 状态表示(核心基础)

本题要求计算最长湍流子数组的长度 。湍流要求相邻比较符号交替翻转,因此需要区分最后一段比较方向。我们定义两个状态:

  • f[i] 表示以 arr[i] 结尾,且最后一段比较为"下降"(即 arr[i-1] > arr[i])的湍流子数组的最大长度

  • g[i] 表示以 arr[i] 结尾,且最后一段比较为"上升"(即 arr[i-1] < arr[i])的湍流子数组的最大长度

2.2 状态转移方程(关键难点)

要延长湍流子数组,当前比较必须与前一个比较方向相反

  • arr[i-1] > arr[i] (当前为下降),则它应接在上一个上升 状态之后,因此 f[i] = g[i-1] + 1

  • arr[i-1] < arr[i] (当前为上升),则它应接在上一个下降 状态之后,因此 g[i] = f[i-1] + 1

  • arr[i-1] == arr[i] ,无法形成湍流,两个状态均保持为 1 (重新开始)。

    该方程完整体现了比较符号交替翻转的要求。

2.3 初始化(边界防护)

每个元素至少可以单独构成一个长度为 1 的湍流子数组,因此 f[i]g[i] 均初始化为 1 (包括 i=0)。

2.4 填表顺序(递推方向)

每个 i 状态仅依赖 i-1 的状态,因此必须从左到右 (即 i 从 1 到 n-1)依次填充两个数组,确保每个状态计算时,其所有前置状态均已就绪

2.5 返回值(目标映射)

题目要求返回最长湍流子数组的长度 ,即所有 f[i]g[i] 中的最大值。我们在递推过程中同步维护 ret,最终返回 ret

三、代码

cpp 复制代码
class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        int n = arr.size();
        if (n == 0) return 0;

        // 1. 创建dp表(两个一维数组)
        // f[i] : 以 arr[i] 结尾,且最后一段比较关系为"下降"(arr[i-1] > arr[i])的湍流子数组的最大长度
        // g[i] : 以 arr[i] 结尾,且最后一段比较关系为"上升"(arr[i-1] < arr[i])的湍流子数组的最大长度
        vector<int> f(n, 1); // 初始化每个元素至少自身长度为1
        vector<int> g(n, 1);

        int ret = 1; // 全局最大湍流长度,至少为1

        // 2. 初始化:dp[0] 已经为1,表示单个元素构成湍流子数组

        // 3. 填表顺序:从左到右(i 从 1 到 n-1),因为 f[i]/g[i] 依赖 f[i-1]/g[i-1]
        for (int i = 1; i < n; i++) {
            // 4. 状态转移方程:
            // 若当前比较为"下降"(前一个元素大于当前):
            //   则可以从上一个"上升"状态(g[i-1])延长,长度加1
            if (arr[i - 1] > arr[i]) {
                f[i] = g[i - 1] + 1;
            }
            // 若当前比较为"上升"(前一个元素小于当前):
            //   则可以从上一个"下降"状态(f[i-1])延长,长度加1
            else if (arr[i - 1] < arr[i]) {
                g[i] = f[i - 1] + 1;
            }
            // 若相等,则不满足湍流条件,f[i]和g[i]保持为1(重新开始)

            // 更新全局最大值
            ret = max(ret, max(f[i], g[i]));
        }

        // 5. 返回值:整个数组中的最长湍流子数组长度
        return ret;
    }
};

四、流程图

五、题目

六、做题思路

6.1 状态表示(核心基础)

本题要求判断字符串 s 能否被字典中的单词拼接而成 。我们定义 dp[i] 表示字符串 s 的前 i 个字符(即 s[0..i-1])能否被拆分成字典中的单词

6.2 状态转移方程(关键难点)

考虑最后一个单词的起始位置 j1 ≤ j ≤ i),那么前缀 s[0..i-1] 可拆分,当且仅当:

  • 前缀 s[0..j-2] 可拆分(即 dp[j-1] == true),并且

  • 子串 s[j-1..i-1](即第 j 到第 i 个字符)在字典中

因此,状态转移方程为:

dp[i] = true 当且仅当存在 j ∈ [1, i],使得 dp[j-1] == trues.substr(j-1, i-j+1) 在字典中

实际编码中,我们i 向前枚举 j ,一旦找到满足条件的 j,立即置 dp[i] = true 并跳出,以提升效率。

6.3 初始化(边界防护)

空串可以被拆分 ,因此 dp[0] = true 。其余 dp[i] 初始化为 false。为方便处理子串,在 s 前添加一个占位字符(如空格),使得 s[j] 对应原始字符串的第 j-1 个字符,这样取子串时 s.substr(j, i-j+1) 表示原始 s[j-1..i-1]

6.4 填表顺序(递推方向)

dp[i] 依赖 dp[j-1](其中 j-1 < i),即更小的前缀长度 。因此必须从左到右 (即 i 从 1 到 n)依次填充 dp 表,确保每个状态计算时,其所有前置状态已就绪

6.5 返回值(目标映射)

题目要求返回整个字符串 s 能否被拆分 ,即长度为 n 的前缀的可拆分性,对应 dp[n] 。因此直接返回 dp[n]

七、代码

cpp 复制代码
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        // 1. 使用哈希集合存储单词,便于 O(1) 查询
        unordered_set<string> hash;
        for (const string& word : wordDict) {
            hash.insert(word);
        }

        int n = s.size();
        // 2. 创建dp表
        // dp[i] 表示字符串 s 的前 i 个字符(即 s[0..i-1])能否被拆分成字典中的单词
        // 为了方便处理边界,我们在 s 前面加一个空格,使索引从 1 开始对应原始字符
        vector<bool> dp(n + 1, false);

        // 3. 初始化:空串可以被拆分
        dp[0] = true;

        // 为了方便取子串,在 s 前面添加一个占位符(空格),这样 s[j] 对应原始第 j-1 个字符
        // 注意:substr(j, len) 中的 j 是 1-based 索引(因为前面加了空格)
        s = ' ' + s;

        // 4. 填表顺序:从左到右(i 从 1 到 n),因为 dp[i] 依赖于更小的索引 dp[j-1]
        for (int i = 1; i <= n; i++) {
            // 内层循环从 i 向前遍历,尝试找最后一个单词的起始位置 j
            // 从后向前可以尽早找到匹配并跳出(优化)
            for (int j = i; j >= 1; j--) {
                // 5. 状态转移方程:
                // 如果前 j-1 个字符可以拆分(dp[j-1] == true),
                // 并且子串 s[j..i] 在字典中,则 dp[i] = true
                if (dp[j - 1] && hash.count(s.substr(j, i - j + 1))) {
                    dp[i] = true;
                    break; // 一旦成立,无需继续尝试其他 j
                }
            }
        }

        // 6. 返回值:dp[n] 表示整个字符串是否能被拆分
        return dp[n];
    }
};

八、流程图

相关推荐
aaPIXa6221 小时前
C++大型项目模块化拆分实战记录
开发语言·c++
cxr8281 小时前
第16章 跨域迁移与能力融合——从专家到通才
人工智能·算法·智能体·hermes
石山代码1 小时前
C++23 新特性在 CLion 中的实战体验
开发语言·c++·c++23
学究天人1 小时前
数学公理体系大全:Comprehensive Collection of Mathematical Axiom Systems(补充卷9)
人工智能·线性代数·算法·数学建模·动态规划·原型模式·抽象代数
Keven_112 小时前
算法札记:图的存储方式
算法·图论
在线OJ的阿川2 小时前
仿muduo库实现高并发服务器
linux·服务器·c++
斯文的八宝粥2 小时前
SERL:让真机强化学习从“难用”走向“可复现”的强化学习框架 ----(4)算法篇(DrQ vs VICE)
算法
yangdaxiageo2 小时前
算法时代的“心智置顶“:《功夫女足》如何构建GEO正向飞轮
人工智能·科技·算法·aigc
坚定学代码2 小时前
C++ SOLID 原则(上):单一职责、开闭原则与里氏替换
c++