美文网首页
leetcode 139. 单词拆分 解题思路

leetcode 139. 单词拆分 解题思路

作者: 问君西游何时还 | 来源:发表于2020-06-26 02:41 被阅读0次

    139. 单词拆分

    给定一个非空字符串 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

    解题思路

    这个题还是有一点难度的,需要明确的是,字典中是存在前缀词的,比如“aaa”,"aaaa"可以同时存在于字典中,所以基本排除了On的可能性。

    第一种思路是利用递归,方法是按从左到右顺序遍历字符串,将当前第i个字符,与之前的字符拼接成单词,在字典中查找,如果字典中存在该单词,那么继续递归遍历s从i到结尾的子字符串,同时还要考虑第i个字符并不是单词结尾的可能,继续向后遍历。一直到整个字符串s的尾部,如果刚好有个单词的结尾在s的尾部,那么结束遍历。
    语言的描述有点抽象,但是代码写起来是很简洁的。

    代码实现如下:

        public static boolean wordBreakHist(String s, List<String> wordDict) {
            char[] arr = s.toCharArray();
            String curWord = "";
            for (int i = 0; i < arr.length; i++) {
                curWord += arr[i];
                if (wordDict.contains(curWord)) {
                    if (i == arr.length - 1) {
                        return true;
                    }
                    if (wordBreak(s.substring(i + 1), wordDict)) {
                        return true;
                    }
                }
            }
            return false;
        }
    

    但是迭代效率太低,跑到这个用例的时候报超时了。

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

    继续优化一下思路,考虑用动态规划来做。
    首先我们知道,当s可以被拆分的时候,s必然是由一个单词结尾的。也就说s是否可拆分,可以简化为,能否找到一个位置i,使得s(0,i)为一个可拆分的字符串,而s(i+1,leng)为一个单词。
    即 dp[length] = max{dp[i] * s(i,leng)}; 其中dp[i]=1表示s(0,i)为一个可拆分的字符串,s(i,leng)=1表示s(i+1,leng)为一个单词。
    根据这个思路,我们对s进行遍历,显然由于每次dp[i]的变化都会影响后续dp的计算,所以我们需要On2的遍历次数。或者说,我们需要对s字符串中,每一个可以组成字符串的子串进行计算,所以需要On2的遍历,代码中s(j,i)即代表每种可能的结尾单词。
    另外,我们需要计算一下dp的初值,显然当s为一个单词时,dp[s]=1
    即 dp[s] = dp[0] * s(0,leng);
    由此我们可以求得dp[0]=1。

    以下是代码实现:

        public static boolean wordBreak(String s, List<String> wordDict) {
            char[] arr = s.toCharArray();
            boolean[] dp = new boolean[arr.length + 1];
            dp[0] = true;
            for (int i = 0; i <= arr.length; i++) {
                for (int j = 0; j < i; j++) {
                    if (dp[j] && wordDict.contains(s.substring(j, i))) {
                        dp[i] = true;
                        break;
                    }
                }
            }
    
            return dp[arr.length];
    
        }
    

    两种方法比较,使用动态规划确实要比迭代快很多。


    image.png

    另附测试代码:

        public static void main(String[] args) {
            List<String> wordDict = new ArrayList<>();
            wordDict.add("aaaa");
            wordDict.add("aaa");
            System.out.println(wordBreak("aaaaaaa", wordDict));
            List<String> wordDict2 = new ArrayList<>();
            String test = "";
            for (int i = 0; i < 10; i++) {
                test += "a";
                String test2 = test;
                wordDict2.add(test2);
            }
            long start = System.currentTimeMillis();
            System.out.println(wordBreak(
                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
                    wordDict2));
            long end = System.currentTimeMillis();
            System.out.println("wordBreak cost "+(end-start));
            start=end;
            System.out.println(wordBreakHist(
                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab",
                    wordDict2));
            end = System.currentTimeMillis();
            System.out.println("wordBreakHist cost "+(end-start));
    
        }
    

    相关文章

      网友评论

          本文标题:leetcode 139. 单词拆分 解题思路

          本文链接:https://www.haomeiwen.com/subject/udkyfktx.html