WordBreakII 的四种解法,先解锁DP+reconstruct 解法

🧩 题目描述

给定一个字符串 s 和一个字符串字典 wordDict,在保证字符串可以被分割成若干个字典中的单词的前提下,返回所有可能的分割方式。

示例: 输入: s = "catsanddog" wordDict = "cat", "cats", "and", "sand", "dog"

输出: "cats and dog", "cat sand dog"

解题思路

两步策略:

  1. 动态规划(DP)+ 记录路径(prev) :判断某一位置是否可以被有效拆分,并记录所有有效拆分路径。
  2. 深度优先搜索(DFS)回溯 :通过 DFS 从 prev 中恢复所有路径,构造出最终结果。

🔢 算法实现步骤

1. 动态规划数组构建

  • 定义:

    • dp[i]:表示前 i 个字符能否被字典中的单词有效切分。
    • prev[j][i]:若 s[j..i-1] 是字典中的单词,并且 dp[j] 为 true,则 prev[j][i] = true。
  • 转移公式:

    ini 复制代码
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < i; j++) {
            if (dp[j] && wordDict.contains(s.substring(j, i))) {
                dp[i] = true;
                prev[j][i] = true;
            }
        }
    }

2. 回溯 DFS 构建句子

  • 从末尾位置 n 开始向前回溯:

    • 若 prev[i][cur] == true,则 s[i..cur] 是有效单词。
    • 将该单词加入路径 path,递归向前继续查找。
    • 到达起点 cur == 0 时,路径完整,反转 path 拼接成一个合法句子。

✅ Java 实现代码

ini 复制代码
public class WordBreakII {

    public List<String> wordBreakII(String s, List<String> wordDict) {
        List<String> result = new ArrayList<>();
        if (s == null || s.length() == 0 || wordDict == null || wordDict.isEmpty()) {
            return result;
        }

        Set<String> set = new HashSet<>(wordDict);
        int n = s.length();
        boolean[] dp = new boolean[n + 1];
        boolean[][] prev = new boolean[n][n + 1];
        dp[0] = true;

        // DP + 记录路径
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && set.contains(s.substring(j, i))) {
                    dp[i] = true;
                    prev[j][i] = true;
                }
            }
        }

        // 回溯构建路径
        List<String> path = new ArrayList<>();
        dfs(s, n, prev, path, result);
        return result;
    }

    private void dfs(String s, int cur, boolean[][] prev, List<String> path, List<String> result) {
        if (cur == 0) {
            StringBuilder sb = new StringBuilder();
            for (int i = path.size() - 1; i >= 0; i--) {
                sb.append(path.get(i)).append(" ");
            }
            sb.deleteCharAt(sb.length() - 1);
            result.add(sb.toString());
            return;
        }

        for (int i = cur - 1; i >= 0; i--) {
            if (prev[i][cur]) {
                path.add(s.substring(i, cur));
                dfs(s, i, prev, path, result);
                path.remove(path.size() - 1);
            }
        }
    }
}

📌 时间与空间复杂度分析

  • 时间复杂度: O(n^2)

    • DP 过程:两重循环扫描子串。
    • DFS:回溯树的最大分支与子问题数有关,最坏情况指数级。
  • 空间复杂度: O(n^2)

    • dp[]、prev[][] 占用主要空间。

✨ 总结

  • 将 可行性判断 与 路径记录 拆分,使代码结构清晰。
  • 使用 二维布尔数组 prev\[\]\[\] 来保存每一个可行的切分点,避免重复计算。
  • 最后用 DFS 回溯路径,还原所有有效的句子。
相关推荐
2301_32241428041 小时前
基于SpringBoot+Vue的休闲零食超市管理系统的设计与实现59004-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·vue.js·spring boot·redis·mysql·决策树·推荐算法
徐小夕3 小时前
花了3个月,我们做了一款 AI 版的“飞书文档”
前端·算法·github
Robert--cao3 小时前
移动底盘与机械臂联合规划:开源算法与工程实现
算法
金銀銅鐵3 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
福建佰胜张工4 小时前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
小豆庄4 小时前
Mac 上给视频人脸打码:本地工具的依赖、追踪与推理管线
算法·视觉检测·视频·敏捷流程·纹理贴图
catoop4 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
星夜离尘564 小时前
MATLAB 工程实验:从相邻块均值手写重叠 Allan 偏差,数清有效项
算法·matlab·均值算法
system_error_x5 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事5 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring