动态规划经典题解:单词拆分(LeetCode 139)

动态规划经典题解:单词拆分(LeetCode 139)

一、题目描述

原题链接
单词拆分

题目大意

给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判断 s 是否可以被空格拆分为一个或多个在字典中出现的单词。字典中的单词可以重复使用。字典中无重复单词。

示例

输入:s = "leetcode", wordDict = "leet", "code"

输出:true(解释:"leetcode" 可拆分为 "leet code")

输入:s = "applepenapple", wordDict = "apple", "pen"

输出:true(解释:"applepenapple" 可拆分为 "apple pen apple")

输入:s = "catsandog", wordDict = "cats", "dog", "sand", "and", "cat"

输出:false

二、解题思路

核心思想:动态规划(DP)

本题的核心是判断字符串的「前缀是否可拆分」,通过动态规划逐步推导所有前缀的可行性,避免重复计算。
1. 状态定义

定义 dpi 表示:字符串 s 的前 i 个字符组成的子串 s0...i-1 是否能被字典中的单词拼接而成。例如:dp4 对应 s 的前 4 个字符 s0...3(即 "leet")是否可拆分。
2. 状态转移方程

对于每个位置 i(从 1 到 s.size()),遍历字典中的每个单词 word:若 word 的长度 len ≤ i(保证子串存在);且 s 中以 i 为结尾、长度为 len 的子串(即 s.substr(i-len, len))等于 word;且 dpi-len 为 true(说明 s 的前 i-len 个字符可拆分);则 dpi = true(前 i 个字符可拆分)。
3. 初始化

dp0 = true:表示空字符串可被拆分(基础条件,用于推导后续前缀。例如,当 word 恰好等于 s0...len-1 时,dplen = dp0 && (子串匹配))。
4. 最终结果

dps.size():表示整个字符串 s 是否可拆分。

三、代码实现

cpp 复制代码
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n = s.size();
        // dp[i]:前i个字符是否可拆分
        vector<bool> dp(n + 1, false);
        // 初始化:空字符串可拆分
        dp[0] = true;
        
        // 遍历所有前缀长度(从1到n)
        for (int i = 1; i <= n; ++i) {
            // 遍历字典中的每个单词
            for (const string& word : wordDict) {
                int word_len = word.size();
                // 条件1:单词长度不超过当前前缀长度
                if (word_len <= i) {
                    // 条件2:子串匹配 + 前i-word_len个字符可拆分
                    if (s.substr(i - word_len, word_len) == word && dp[i - word_len]) {
                        dp[i] = true;
                        // 无需继续遍历其他单词,直接跳出内层循环
                        break;
                    }
                }
            }
        }
        // 返回整个字符串是否可拆分
        return dp[n];
    }
};

四、边界情况与测试用例

边界情况

s 为空字符串:题目规定 s 非空,无需处理;

wordDict 中存在与 s 完全匹配的单词:如 s = "a", wordDict = "a",dp1 = dp0 && (s.substr(0,1) == "a") = true;单词长度大于 s 长度:如 s = "a", wordDict = "aa",内层循环条件 word_len <= i 不满足,dp1 保持 false。

测试用例验证

示例 1:s = "leetcode", wordDict = "leet", "code"

i=4:单词 "leet" 长度 4 ≤ 4,s.substr(0,4) = "leet" 匹配,dp0 = true → dp4 = true;i=8:单词 "code" 长度 4 ≤ 8,s.substr(4,4) = "code" 匹配,dp4 = true → dp8 = true;

返回 true。

示例 2:s = "catsandog", wordDict = "cats", "dog", "sand", "and", "cat"

前 7 个字符 "catsand" 可拆分为 "cats + and"(dp7 = true);但 i=9(整个字符串长度)时,遍历所有单词:

"dog" 长度 3 ≤ 9,s.substr(6,3) = "dog" 匹配,但 dp6 = false;其他单词均不满足条件 → dp9 = false;

返回 false。

五、 类似题目

LeetCode 140. 单词拆分 II

LeetCode 472. 连接词

相关推荐
l1564694816 分钟前
突围!图文混合知识库难以解析,Kimi-K3 原生视觉架构深耕知识工作,DMXAPI 统一接口,缩短项目开发周期
java·大数据·开发语言
旅僧21 分钟前
Q-learning(自用)
python·机器学习
啦啦啦啦啦zzzz35 分钟前
算法:贪心算法
c++·算法·leetcode·贪心算法
Python大数据分析@40 分钟前
曝梁文锋称「一度不想维护C端用户,奈何赶不走」,DeepSeek真不需要C端用户吗?可能会有啥影响吗?
python
布吉岛的石头1 小时前
Java 程序员第 45 阶段11:网关统一路由大模型接口,配合 Nacos 配置治理,灰度路由:基于Nacos元数据实现大模型服务灰度发布与权重路由
java·服务发现
日拱一卒——功不唐捐1 小时前
红黑树删除(C语言)
c语言·数据结构
我命由我123452 小时前
Android 开发问题:Cannot resolve method ‘repeat‘ in ‘String‘
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
艾斯特_2 小时前
工作流与多Agent协作:LangGraph、MCP和A2A的应用分层
人工智能·python·ai
IT小盘2 小时前
04-大模型流式输出原理-SSE与Python实现
开发语言·网络·人工智能·python
baozhengw2 小时前
信创改造-SpringBootJar包改为WAR包部署Tomcat
java·tomcat