跳到主要内容

LeetCode 139. 单词拆分

本节目标

以可达前缀为状态,判断能否在末尾接上一个字典词。

这是序列与字符串动态规划中布尔前缀可达性的母题。

题意与约束

判断字符串能否被拆成一个或多个字典单词,字典中的单词可重复使用。

第一反应与重复子问题

如果较短前缀已经可拆,且末尾恰好是一个字典词,较长前缀也可拆;同一前缀是否可达会被多个后缀查询。

状态定义与转移推导

dp[i] 表示长度为 i 的前缀能否拆分。dp[0] 为真;枚举词 word,若 dp[i-|word|] 为真且末尾匹配该词,就令 dp[i] 为真。

正确性依据

任一完整拆分都有最后一个字典词,去掉它后是可达前缀;反之,在可达前缀后接匹配词必构成合法拆分。

样例执行过程

cars 可先拆为 ca,再接 rs;不能因 car 是前缀就贪心地提前失败。

代码实现

C++17
#include <string>
#include <vector>
using namespace std;

class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
vector<bool> reachable(s.size() + 1, false);
reachable[0] = true;
for (int end = 1; end <= static_cast<int>(s.size()); end++) {
for (const string& word : wordDict) {
int start = end - static_cast<int>(word.size());
if (start >= 0 && reachable[start] && s.compare(start, word.size(), word) == 0) {
reachable[end] = true;
break;
}
}
}
return reachable[s.size()];
}
};

复杂度分析

设字符串长度为 n、字典总字符量为 L,朴素末尾比较的时间约为 O(nL),状态空间 O(n)

边界与易错点

  • 空前缀必须设为可达,才能匹配开头的单词。
  • 不要只按最长或最短单词贪心,后缀可能需要另一种切分。

模式迁移

最少切分数、句子枚举和字符串匹配均可先用前缀可达性过滤无效状态。