单词拆分

这是leetcode上的一道题

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-break
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

刚开始的时候,我的思路是这样的:如果原字符串s能被拆分成wordDict里的单词,那么wordDict里的单词肯定就是原字符串s的子串。用KMP算法,将wordDict里面的单词和原字符串匹配,如果所有单词都不能匹配,则表示原字符串不能被拆分成wordDict里的单词,返回false;如果可以找到一个单词,是字符串s的子串,那么将s去掉这个子串,继续验证去掉这个子串的s能不能被wordDict里的单词匹配。当最后s被拆分为一个空字符串后,说明这个单词是可以被拆分的。

代码如下:

 1 class Solution {
 2     public boolean wordBreak(String s, List<String> wordDict) {
 3        while(!s.equals("")){
 4             boolean flag = false;
 5             for(String word : wordDict){
 6                 int i = KMP(s,word);
 7                 int j = 0;
 8                 if(i != -1){
 9                     flag = true;
10                     j = i + word.length() - 1;
11                     s = s.substring(0,i)+s.substring(j+1,s.length());
12                     break;
13                 }
14             }
15             if(!flag)
16                 return false;
17         }
18         return true;
19     }
20 
21     /*
22      * KMP算法的实现
23    */
24     public int KMP(String str,String patten){
25         char[] s = str.toCharArray();
26         char[] ps = patten.toCharArray();
27         int i = 0;
28         int j = 0;
29         int[] next =  getNext(patten);
30         while(i < s.length && j < ps.length){
31             if(j == -1 || s[i] == ps[j]){
32                 i++;
33                 j++;
34             }else{
35                 j = next[j];
36             }
37         }
38         if( j == ps.length)
39             return i-j;
40         else
41             return -1;
42     }
43     /*
44      * 获取next[]数组(KMP算法里很关键的一部分
45     */
46     public int[] getNext(String patten){
47         char[] p = patten.toCharArray();
48         int[] next = new int[patten.length()];
49         next[0] = -1;
50         int k = -1;
51         for(int i = 1; i < p.length-1; i++){
52             if(k == -1 || p[i] == p[k]) {
53                 next[++i] = ++k;
54             }else
55                 k = next[k];
56         }
57         return next;
58     }
59 }

示例中的三个都通过了验证,但是这个解法是错误的。以以下这个例子为例

输入:"cars"
         ["car","ca","rs"]

输出:false

预期:true

为什么会出现这个错误呢?这是因为在代码中,我是用forEach遍历wordDict中的每个单词,由于wordDict中的"car"被遍历到,"car"又是"cars"的子串,"cars"就被拆分成"car"和"s"的组合,但是wordDict中没有"s",因此程序判定"cars"不能由wordDict中的单词组成,返回false。但实际上,"cars"可以由"ca"和"rs"组成,程序没有考虑到这个,因此出错。也就是说,以上的程序没有考虑到一个字符串可能有多种组合。

 

介绍下官方题解中的使用宽度优先搜索来解这道题。

官方题解的这个方法和我的方法的区别在于,它在搜索到"cars"不能由"car"和"s"组成后,并没有结束程序,而是继续搜索wordDict中是否还有其它组合。

官方题解的代码如下:

 1     public boolean wordBreak(String s, List<String> wordDict) {
 2         Set<String> wordDictSet=new HashSet(wordDict);
 3         Queue<Integer> queue = new LinkedList<>();
 4         int[] visited = new int[s.length()];
 5         queue.add(0);
 6         while (!queue.isEmpty()) {
 7             int start = queue.remove();
 8             if (visited[start] == 0) {
 9                 for (int end = start + 1; end <= s.length(); end++) {
10                     if (wordDictSet.contains(s.substring(start, end))) {
11                         queue.add(end);
12                         if (end == s.length()) {
13                             return true;
14                         }
15                     }
16                 }
17                 visited[start] = 1;
18             }
19         }
20         return false;
21     }
22 
23 作者:LeetCode
24 链接:https://leetcode-cn.com/problems/word-break/solution/dan-ci-chai-fen-by-leetcode/
25 来源:力扣(LeetCode)
26 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

官方的这个代码有个优化的空间,就是将visited的类型由int改为boolean,这样可以省部分空间。

 

posted @ 2019-10-28 11:15  ShangyiChen  阅读(557)  评论(0编辑  收藏  举报