LeetCode(139 & 140):单词拆分 I II Word Break I II(Java)

234 篇文章 1 订阅
177 篇文章 0 订阅

2019.8.4 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

基础版是一道完全背包问题,可以用动态规划的方法求解。

建立dp数组,dp[i]表示字典内的字符可以组成0至i-1的s的子串。遍历字符串的每一位更新dp。注意0-1 背包和完全背包在实现上的不同之处是,0-1
背包对物品的迭代是在最外层,而完全背包对物品的迭代是在最里层。

升级版的问题也可以用动态规划或记忆化递归做,但是极其容易在以下用例上发生超时。

"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]

可以考虑复用基础版的代码,先求出能够完成拆分的子串的信息,再用这个信息对规划过程或递归过程剪枝。


传送门:单词拆分

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

The same word in the dictionary may be reused multiple times in the segmentation.

You may assume the dictionary does not contain duplicate words.

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

拆分时可以重复使用字典中的单词。

你可以假设字典中没有重复的单词。

示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重复使用字典中的单词。

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



import java.util.List;

/**
 *
 * Given a non-empty string s and a dictionary wordDict containing a list of non-empty words,
 * determine if s can be segmented into a space-separated sequence of one or more dictionary words.
 * Note:
 * The same word in the dictionary may be reused multiple times in the segmentation.
 * You may assume the dictionary does not contain duplicate words.
 * 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
 * 说明:
 * 拆分时可以重复使用字典中的单词。
 * 你可以假设字典中没有重复的单词。
 *
 */

public class WordBreak {
    public boolean wordBreak(String s, List<String> wordDict) {
        //dp[i]表示字典内的字符可以组成0至i-1的s的子串
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        //0-1 背包和完全背包在实现上的不同之处是,0-1 背包对物品的迭代是在最外层,而完全背包对物品的迭代是在最里层。
        for(int i = 1; i < s.length() + 1; i++){
            for(String str : wordDict){
                int len = str.length();
                if(i >= len && dp[i - len] && str.equals(s.substring(i - len, i))){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}





传送门:单词拆分 II

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.

给定一个非空字符串 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"]
输出:
[]


import java.util.*;

/**
 *
 * Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word.
 * Return all such possible sentences.
 * Note:
 * The same word in the dictionary may be reused multiple times in the segmentation.
 * You may assume the dictionary does not contain duplicate words.
 * 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。
 * 说明:
 * 分隔时可以重复使用字典中的单词。
 * 你可以假设字典中没有重复的单词。
 *
 */

public class WordBreakII {

    //动态规划(超时)
    //超时用例:
    //"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    //["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
    public List<String> wordBreak(String s, List<String> wordDict) {
        HashSet<String> dict = new HashSet<>();
        for(String str : wordDict){
            dict.add(str);
        }

        //ismatch[i]表示字典内的字符能否组成0至i-1的s的子串
        boolean[] ismatch = new boolean[s.length() + 1];
        ismatch[0] = true;
        for(int i = 1; i < s.length() + 1; i++){
            for(String str : wordDict){
                int len = str.length();
                if(i >= len && ismatch[i - len] && str.equals(s.substring(i - len, i))){
                    ismatch[i] = true;
                    break;
                }
            }
        }

        //dp[i]表示字典内的字符组成0至i-1的s的字符串
        ArrayList<String>[] dp = new ArrayList[s.length() + 1];
        dp[0] = new ArrayList<>();
        dp[0].add("");

        for(int i = 1; i <= s.length(); i++){
            //跳过不能构成字符串的拆分项
            dp[i] = new ArrayList<>();
            if(!ismatch[i]){
                continue;
            }
            for(int j = 0; j < i; j++){
                //0-j有符合的字符串,且j-i构成的单词str在字典中,那么可以对dp[j]中的所有字符串加上str
                if(!ismatch[j]) {
                    continue;
                }
                if(dict.contains(s.substring(j, i))){
                    String str = s.substring(j, i);
                    for(String string : dp[j]){
                        dp[i].add(string + (string.equals("") ? "" : " ") + str);
                    }
                }
            }
        }

        return dp[s.length()];
    }

    //Memorized DFS
    public List<String> wordBreak2(String s, List<String> wordDict){
        int len = s.length();
        //将所有word存入哈希表
        int maxLen = 0;
        HashSet<String> dict = new HashSet<>();
        for(String str : wordDict){
            dict.add(str);
            maxLen = str.length() > maxLen ? str.length() : maxLen;
        }
        //动态规划,预先判定字典内的字符能否组成0至i-1的s的子串
        boolean[] dp = new boolean[len + 1];
        dp[0] = true;
        for(int i = 1; i <= len; i++){
            for(int j = Math.max(0, i - maxLen); j < i; j++){
                if(dp[j] && dict.contains(s.substring(j , i))){
                    dp[i] = true;
                    break;
                }
            }
        }

        return DFS(s, dict, new HashMap<>(), dp);
    }

    public ArrayList<String> DFS(String s, HashSet<String> dict, HashMap<String, ArrayList<String>> map, boolean[] dp){
        //如果字符串s能够由多个word拆分而成,且已经计算并存储进map,直接返回。
        if(map.containsKey(s)){
            return map.get(s);
        }
        ArrayList<String> result = new ArrayList<>();
        if(s.length() == 0){
            return result;
        }
        //如果字符串s能够直接构成字典中的单词,则直接将其加入结果列表
        if(dict.contains(s)){
            result.add(s);
        }

        //i从后往前截断字符串,将其分为pre(0-i)与cur(i-end)
        int end = s.length();
        for(int i = end - 1; i >= 0; i--){
            String cur = s.substring(i, end);
            //如果cur在dict中,且pre能够构成拆分子串,则递归对pre进行计算,将计算结果拼接成字符串,存储在map中记忆,并返回。
            if(dict.contains(cur) && dp[i]){
                ArrayList<String> list = DFS(s.substring(0, i), dict, map, dp);
                for(String str : list){
                    result.add(str + " " + cur);
                }
            }
        }
        map.put(s, result);
        return result;
    }

    //Set版
    public class Solution {

        public List<String> wordBreak(String s, Set<String> wordDict) {
            return word_Break(s, wordDict, 0);
        }
        HashMap<Integer, List<String>> map = new HashMap<>();

        public List<String> word_Break(String s, Set<String> wordDict, int start) {
            if (map.containsKey(start)) {
                return map.get(start);
            }
            LinkedList<String> res = new LinkedList<>();
            if (start == s.length()) {
                res.add("");
            }
            for (int end = start + 1; end <= s.length(); end++) {
                if (wordDict.contains(s.substring(start, end))) {
                    List<String> list = word_Break(s, wordDict, end);
                    for (String l : list) {
                        res.add(s.substring(start, end) + (l.equals("") ? "" : " ") + l);
                    }
                }
            }
            map.put(start, res);
            return res;
        }
    }
}




#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值