算法---LeetCode 140. 单词拆分 II

1. 题目

原题链接

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的
句子。
说明:

分隔时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。

示例 1:
输入:
s = “catsanddog”
wordDict = [“cat”, “cats”, “and”, “sand”, “dog”]
输出:
[
“cats and dog”,
“cat sand dog”
]

示例 2:
输入:
s = “pineapplepenapple”
wordDict = [“apple”, “pen”, “applepen”, “pine”, “pineapple”]
输出:
[
“pine apple pen apple”,
“pineapple pen apple”,
“pine applepen apple”
]
解释: 注意你可以重复使用字典中的单词。

示例 3:
输入:
s = “catsandog”
wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出:
[]

Related Topics 动态规划 回溯算法
👍 463 👎 0

2. 题解

2.1 解法1: DFS递归

递归函数:

  • 作用: 不停切分字符串, 返回切分的结果
  • 参数: 切分开始的下标
  • 返回值: [“www”,“www”…]
  • 终止条件: start >= s.length(), 说明start越界时,即剩余子串为"",切不出单词了,整个s串一路被切成单词了。结束递归,返回空串[""], 因为题目要求若无结果返回空
    class Solution {
        Set<String> set;
        String s;
        public List<String> wordBreak(String s, List<String> wordDict) {
            this.s = s;
            this.set = new HashSet<>(wordDict);
            return dfs(0);
        }
        public List<String> dfs(int start) {
            if (start >= s.length()) {
                return new ArrayList<String>() {{
                    add("");
                }};
            }
            List<String> ans = new ArrayList<>();
            // 开始向下搜索
            for (int i = start + 1; i <= s.length(); i++) {
                String curr = s.substring(start, i);
                if (set.contains(curr)) {
                    // 切分后面的字符串得到结果
                    List<String> restAns = dfs(i);
                    // 每次切分得到一种结果就将其加入到结果集中
                    for (String restAn : restAns) {
                        // 拼接一种结果, 加入结果集
                        if (!restAn.equals("")){
                            String temp=curr+" "+restAn;
                            ans.add(temp);
                        } else {
                            // 否则说明已经分割到结尾, 将当前分割的单词加入结果集
                            ans.add(curr);
                        }
                    }
                }
            }
            return ans;
        }
    }
优化:

由于中间会产生大量重复计算, 可以用 map 或数组存储计算结果,数组索引为指针位置,值为子调用的结果。下次遇到相同的子问题时,直接返回 memo 中的缓存值,而不是落入重复的递归。

    class Solution {
        Set<String> set;
        String s;
        Map<Integer, List<String>> map;

        public List<String> wordBreak(String s, List<String> wordDict) {
            this.s = s;
            this.set = new HashSet<>(wordDict);
            map = new HashMap<>();
            return dfs(0);
        }

        public List<String> dfs(int start) {
            if (map.containsKey(start)) {
                return map.get(start);
            }
            if (start >= s.length()) {
                return new ArrayList<String>() {{
                    add("");
                }};
            }
            List<String> ans = new ArrayList<>();
            // 开始向下搜索
            for (int i = start + 1; i <= s.length(); i++) {
                String curr = s.substring(start, i);
                if (set.contains(curr)) {
                    // 切分后面的字符串得到结果
                    List<String> restAns = dfs(i);
                    // 每次切分得到一种结果就将其加入到结果集中
                    for (String restAn : restAns) {
                        // 拼接一种结果, 加入结果集
                        if (!restAn.equals("")) {
                            String temp = curr + " " + restAn;
                            ans.add(temp);
                        } else {
                            // 否则说明已经分割到结尾, 将当前分割的单词加入结果集
                            ans.add(curr);
                        }
                    }
                }

            }
            map.put(start, ans);
            return ans;
        }


    }

参考: 「手画图解」单词拆分 II | 记忆化递归 | 思路剖析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值