139. 单词拆分(Word Break)

给定字符串 s 和字符串字典 wordDict,判断 s 是否可以被拆分为一个或多个字典中的单词。

特点:

  • 字典单词 可以重复使用

  • 不要求使用所有字典单词


方法一:暴力 DFS(递归)

思路

从字符串开头开始:

  1. 枚举前缀

  2. 如果前缀在字典中

  3. 递归判断剩余部分

例如:

复制代码
applepenapple

apple | penapple
      pen | apple

如果能拆分到最后返回 true


C 代码

复制代码
#include <stdbool.h>
#include <string.h>

bool inDict(char* s, int start, int len, char** wordDict, int wordDictSize)
{
    for(int i = 0; i < wordDictSize; i++)
    {
        if(strlen(wordDict[i]) == len &&
           strncmp(s + start, wordDict[i], len) == 0)
            return true;
    }
    return false;
}

bool dfs(char* s, int start, int len, char** wordDict, int wordDictSize)
{
    if(start == len) return true;

    for(int i = start + 1; i <= len; i++)
    {
        if(inDict(s, start, i - start, wordDict, wordDictSize) &&
           dfs(s, i, len, wordDict, wordDictSize))
            return true;
    }

    return false;
}

bool wordBreak(char* s, char** wordDict, int wordDictSize)
{
    return dfs(s, 0, strlen(s), wordDict, wordDictSize);
}

复杂度

复制代码
时间复杂度:O(2^n)
空间复杂度:O(n)

因为大量重复计算,容易 超时


方法二:DFS + 记忆化搜索(剪枝)

思路

暴力 DFS 的问题:

复制代码
重复计算同一个子问题

例如:

复制代码
applepenapple
       apple

我们可以记录:

复制代码
start 位置是否可以拆分

用数组 memo[] 记录状态:

复制代码
0 = 未计算
1 = 可以拆分
-1 = 不可以拆分

C 代码

复制代码
#include <stdbool.h>
#include <string.h>

int memo[305];

bool inDict(char* s, int start, int len, char** wordDict, int wordDictSize)
{
    for(int i = 0; i < wordDictSize; i++)
    {
        if(strlen(wordDict[i]) == len &&
           strncmp(s + start, wordDict[i], len) == 0)
            return true;
    }
    return false;
}

bool dfs(char* s, int start, int len, char** wordDict, int wordDictSize)
{
    if(start == len) return true;

    if(memo[start] != 0)
        return memo[start] == 1;

    for(int i = start + 1; i <= len; i++)
    {
        if(inDict(s, start, i - start, wordDict, wordDictSize) &&
           dfs(s, i, len, wordDict, wordDictSize))
        {
            memo[start] = 1;
            return true;
        }
    }

    memo[start] = -1;
    return false;
}

bool wordBreak(char* s, char** wordDict, int wordDictSize)
{
    memset(memo, 0, sizeof(memo));
    return dfs(s, 0, strlen(s), wordDict, wordDictSize);
}

复杂度

复制代码
时间复杂度:O(n² * m)
空间复杂度:O(n)

方法三:动态规划(最经典)

这是 LeetCode 官方最推荐方法


DP 思路

定义:

复制代码
dp[i] 表示 s[0..i-1] 是否可以拆分

初始化:

复制代码
dp[0] = true

状态转移:

复制代码
dp[i] = dp[j] && s[j..i-1] 在字典中

其中:

复制代码
0 ≤ j < i

C 代码

复制代码
#include <stdbool.h>
#include <string.h>

bool inDict(char* s, int start, int len, char** wordDict, int wordDictSize)
{
    for(int i = 0; i < wordDictSize; i++)
    {
        if(strlen(wordDict[i]) == len &&
           strncmp(s + start, wordDict[i], len) == 0)
            return true;
    }
    return false;
}

bool wordBreak(char* s, char** wordDict, int wordDictSize)
{
    int n = strlen(s);

    bool dp[n + 1];
    memset(dp, false, sizeof(dp));

    dp[0] = true;

    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j < i; j++)
        {
            if(dp[j] && inDict(s, j, i - j, wordDict, wordDictSize))
            {
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

复杂度

复制代码
时间复杂度:O(n² * m)
空间复杂度:O(n)

方法四:DP + 哈希优化(最优解)

优化点:

1️⃣ 使用 HashSet 加速查找

2️⃣ 限制单词最大长度

题目给出:

复制代码
wordDict[i].length ≤ 20

因此:

复制代码
j >= i - 20

减少无效判断。


优化版 C 代码

复制代码
#include <stdbool.h>
#include <string.h>

bool inDict(char* s, int start, int len, char** wordDict, int wordDictSize)
{
    for(int i = 0; i < wordDictSize; i++)
    {
        if(strlen(wordDict[i]) == len &&
           strncmp(s + start, wordDict[i], len) == 0)
            return true;
    }
    return false;
}

bool wordBreak(char* s, char** wordDict, int wordDictSize)
{
    int n = strlen(s);

    bool dp[n + 1];
    memset(dp, false, sizeof(dp));

    dp[0] = true;

    for(int i = 1; i <= n; i++)
    {
        for(int j = i - 20; j < i; j++)
        {
            if(j >= 0 && dp[j] &&
               inDict(s, j, i - j, wordDict, wordDictSize))
            {
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

DP过程图解

示例:

复制代码
s = "leetcode"
dict = ["leet","code"]

索引:

复制代码
0 1 2 3 4 5 6 7 8
l e e t c o d e

DP数组:

复制代码
T F F F T F F F T

解释:

复制代码
dp[4] = true  (leet)
dp[8] = true  (code)

面试总结

最推荐回答顺序:

复制代码
1 暴力DFS
2 DFS + 记忆化
3 动态规划(重点)
4 DP优化

面试官最希望听到:

复制代码
DP 状态定义 + 状态转移
相关推荐
吞下星星的少年·-·3 分钟前
The 2026 ICPC Asia East Continent Online Contest (II)(构造)
数据结构·算法
派葛穆26 分钟前
Lerobot -飞特舵机ST-3215双舵机联动(ESP_NOW)
c语言
零依赖极客1 小时前
Day 6·1 ARMv8.2 dotprod——vdotq_s32 一条指令做 4 个点积
c语言·开发语言·人工智能·矩阵·arm·vllm
stolentime1 小时前
洛谷P10515 转圈题解
c++·算法·数学建模·贪心算法
和裕2 小时前
蜂窝板 vs 七层瓦楞重型纸箱:大件工业设备运输性能与成本全对比
大数据·运维·网络·人工智能·算法
衡石科技3 小时前
Data_Agent记忆机制与经验复用衡石分析智能体会话记忆与长期学习技术解析
人工智能·科技·学习·算法·企业级bi
Msshu1234 小时前
2~5串锂电快充优选|XSP36 Type‑C升压/升降压快充管理芯片
数据结构·随机森林·贪心算法·排序算法·线性回归·启发式算法
shirsl4 小时前
算法 Day 2 滑动窗口 + 栈 / 单调栈
开发语言·python·算法
老王爱玩车5 小时前
深入理解指针2
c语言·开发语言·学习
土司大王5 小时前
LeetCode hot100 回溯专题总结:Java 通用模板、决策树模型
java·算法·leetcode·决策树