140. Word Break II

Lc-140
categories: [LeetCode]
tags: [Dynamic Programming, DFS, hard]

140. Word Break II

题目大意:

给定一个非空字符串和一个字典包含一个非空的 list of non-empty words
在字符串中加上空格使字符串变成一个句子,使用空格分开的每个单词都能在字典中找到
返回符合条件的所有可能的组合

注意:
同一个单词在字典中可以被使用多次,并且字典中没有重复的单词

解题思路:

首先这道题是一道DP和DFS结合的题,我们将字典中的所有单词放入一个HashSet中保证每一个单词都是不同的
同时我们创建一个boolean类型且长度为s.length()+1的数组dp表示
长度为i的字符串s的前缀可以拆分成字典中的单词
dp[0]为true,因为代表长度为0的字符串
接下来我们利用两个 for 循环去遍历不同长度的前缀字符串是否可以在hashSet中找到
如果前缀字符串substring(left,right)可以在HashSet中找到,且left之前的字符串也为true, 
则dp[right]为true,并break,因为无需判断其他的了

接下来我们初始化一个list<string> ans保存最终结果
如果dp[len]为true表示长度为len的字符串可以再字典中找到对应的单词
创建一个path去保存字典中能组成的单词, 然后用dfs进行搜索

在dfs搜索中, 如果当前字符串长度len为0,则将现有的path放入到ans当中,
并用String.join(" ", path)将path用空格分开
如果长度不为空, 我们用for loop从最后一个字母开始循环,
用substring(i, len)取不同长度的字符串作为后缀,
如果当前所取后缀在wordSet中,并且dp[i]为true,说明i之前的字符串也可以在wordSet中找到,
我们将此时的后缀字符串加在path中,然后进行dfs,此时dfs中的len 为 i
然后我们再用removeFirst将之前加进去的后缀删掉
这样以当前长度为后缀的字符串和剩下的长度的字符串就组成一个sentence的组合
然后进行下一个i-- 的for 循环直到i=0

注意:

class Gfg1 { 
    public static void main(String args[]) { 
        // delimiter is "<" and elements are "Four", "Five", "Six", "Seven" 
        String gfg1 = String.join(" < ", "Four", "Five", "Six", "Seven"); 

        System.out.println(gfg1); 
    } 
} 


// Output: Four < Five < Six < Seven

复杂度:

Time Coplexity: 
Space Complexity:

Code示例:

class Solution {
 public List<String> wordBreak(String s, List<String> wordDict) {
        // 为了快速判断一个单词是否在单词集合中,需要将它们加入哈希表
        Set<String> wordSet = new HashSet<>(wordDict);
        int len = s.length();

        // 第 1 步:动态规划计算是否有解
        // dp[i] 表示「长度」为 i 的 s 前缀子串可以拆分成 wordDict 中的单词
        // 长度包括 0 ,因此状态数组的长度为 len + 1
        boolean[] dp = new boolean[len + 1];
        // 0 这个值需要被后面的状态值参考,如果一个单词正好在 wordDict 中,dp[0] 设置成 true 是合理的
        dp[0] = true;

        for (int right = 1; right <= len; right++) {
            // 如果单词集合中的单词长度都不长,从后向前遍历是更快的
            for (int left = right - 1; left >= 0; left--) {
                // substring 不截取 s[right],dp[left] 的结果不包含 s[left]
                if (wordSet.contains(s.substring(left, right)) && dp[left]) {
                    dp[right] = true;
                    // 这个 break 很重要,一旦得到 dp[right] = True ,不必再计算下去
                    break;
                }
            }
        }

        // 第 2 步:回溯算法搜索所有符合条件的解
        List<String> res = new ArrayList<>();
        if (dp[len]) {
            Deque<String> path = new ArrayDeque<>();
            dfs(s, len, wordSet, dp, path, res);
            return res;
        }
        return res;
    }

    /**
     * s[0:len) 如果可以拆分成 wordSet 中的单词,把递归求解的结果加入 res 中
     *
     * @param s
     * @param len     长度为 len 的 s 的前缀子串
     * @param wordSet 单词集合,已经加入哈希表
     * @param dp      预处理得到的 dp 数组
     * @param path    从叶子结点到根结点的路径
     * @param res     保存所有结果的变量
     */
    private void dfs(String s, int len, Set<String> wordSet, boolean[] dp, Deque<String> path, List<String> res) {
        if (len == 0) {
            res.add(String.join(" ",path));
            return;
        }

        // 可以拆分的左边界从 len - 1 依次枚举到 0
        for (int i = len - 1; i >= 0; i--) {
            String suffix = s.substring(i, len);
            if (wordSet.contains(suffix) && dp[i]) {
                path.addFirst(suffix);
                dfs(s, i, wordSet, dp, path, res);
                path.removeFirst();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值