前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【每日一题】- leetcode- 139. 单词拆分

【每日一题】- leetcode- 139. 单词拆分

作者头像
程序员小王
发布2019-09-05 17:44:34
8120
发布2019-09-05 17:44:34
举报
文章被收录于专栏:架构说架构说

题目描述

  • 来源:https://github.com/wangcy6/leetcode/issues/7
  • 难度:中等

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,

判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

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

想法1

  • 测试用例
代码语言:javascript
复制
 判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
 
 输入: s = "applepenapple", wordDict = ["apple", "pen"]
 输出: true
 解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
  • 步骤描述

为了找到解,我们可以检查字典单词中每一个单词的可能前缀,如果在字典中出现过,那么去掉这个前缀后剩余部分回归调用。

同时,如果某次函数调用中发现整个字符串都已经被拆分且在字典中出现过了,函数就返回 true 。

  • 复杂度

字典虽然是顺序遍历,但是只有11个元素.忽略不计,耗时在哪里呢?

代码语言:javascript
复制
 1. 时间复杂度:O(n^n)
  考虑最坏情况 s = {aaaaaaa} 。每一个前缀都在字典中,此时回溯树的复杂度会达到 n^n
 
 2. 空间复杂度:O(n) 。回溯树的深度最深达到 n。
  • 我的错误理解
  1. 字符串拆分任意字符 需要o(n2)就可以了?
代码语言:javascript
复制
 n-1+n-2+n-3+ .....+ 3 +2 +1 =(1+n)*n/2
 =O(n^n)
  • 参考超时代码golang
代码语言:javascript
复制
 func wordBreak(s string, wordDict []string) bool {
     
     return helper(s,wordDict,0)
     
 }
 
 
 func helper(s string,wordDict []string,start int) bool {
     
     if start >=len(s) {
         return true
     }
     
     for i:=start;i < len(s);i++{
         
         if getWord(s[start:i+1],wordDict) ==true && helper(s,wordDict,i+1) ==true {
     
             return true
         }
     }
     
     return false
 }
 
 func getWord(word string,wordDict []string) bool {
     
     for i:=0; i < len(wordDict);i++ {
         if  word == wordDict[i] {
             return true
         }
         
     }
     
     return false
 }

想法2

  • 步骤描述在想法1的基础上,每次拆分重复利用以前计算结果。这个如何实现呢,拆分每个字符 保存起来每当访问到已经访问过的后缀串,直接用 memomemo 数组中的值返回而不需要继续调用函数。
  • 参考代码(java)
代码语言:javascript
复制
 public class Solution {
     public boolean wordBreak(String s, List<String> wordDict) {
         return word_Break(s, new HashSet(wordDict), 0, new Boolean[s.length()]);
     }
     public boolean word_Break(String s, Set<String> wordDict, int start, Boolean[] memo) {
         if (start == s.length()) {
             return true;
         }
         if (memo[start] != null) {
             return memo[start];
         }
         for (int end = start + 1; end <= s.length(); end++) {
             if (wordDict.contains(s.substring(start, end)) && word_Break(s, wordDict, end, memo)) {
                 return memo[start] = true;
             }
         }
         return memo[start] = false;
     }
 }

想法3

  • 测试用例
代码语言:javascript
复制
 输入: s = "leetcode", wordDict = ["leet", "code"]
 输出: true
 解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。

wordBreak(“leetcode”)过程

判断 wordBreak(“leet”)作用,依赖下层:“leet”拆分任意子串。同时为上层 提供方面

  • 步骤描述题目的要求是:判定 非空字符串 s 是否可以 (被空格)拆分为(一个或多个在字典中出现的 )单词。

定义 dp[r] 以 s[r-1] 结尾的 子字符串 是否可以被空格拆分为一个或多个在字典中出现的单词。

  • 复杂度分析

时间复杂度:O(n^2) dp 数组需要两重循环。

空间复杂度:O(n)。dpdp 数组的长度是 n+1n+1 。

  • golang
代码语言:javascript
复制
 func wordBreak(s string, wordDict []string) bool {
     
     //题目中说非空字符串,以下 assert 一定通过
     if len(s)  == 0 {
         return true 
     }
     
     // 状态定义:长度为 i 的子串可以被空格拆分为一个或多个在字典中出现的单词
     dp:=make([]bool,len(s)+1)
     // 这个状态的设置非常关键,说明前部分的字符串已经在 wordSet 中
     dp[0] =true // s 是空字符串
     
     // 判断逻辑
     //使用 r 表示右边界,可以取到
     //使用 l 表示左边界,也可以取到
     //[0 AA left BBB right]
     //拆分n个单词
     for right:=1;right<=len(s);right++ {
         
         for left:=0;left < right;left++ {
             //同时为上层下一次计算 提供方面
             // dp[left] 写在前面会更快一点,否则还要去切片,然后再放入 hash 表判重
             if dp[left] ==true && getWord(s[left:right],wordDict) ==true {
                 dp[right] =true //依赖下层:子串,判断子串只要一个存在在
                  // 这个 break 很重要,一旦得到 dp[r] = True ,循环不必再继续
                 break
             }
         }
     }
     
     return dp[len(s)]
 }
 
 
 
 
 func getWord(word string,wordDict []string) bool {
     
     for i:=0; i < len(wordDict);i++ {
         if  word == wordDict[i] {
             return true
         }
         
     }
     
     return false
 }

扩展总结

字符字符串s假如拆分s1和s2 。如果s1和s2都存在 最完美了,结束。如果s1和s2都不存在,如何继续判断呢?

动态规划采用的是 不断拆分前缀s1,直到遇到匹配的s2回朔采用的不断拆分后缀s2,直到遇到匹配的s1减少递归次数。

  • 想法1 是通过无记忆回朔方式,直接超时
  • 想法2 是通过有记忆回朔方式
  • 想法3 是通过动态规划方式

画外音:

也就是在前面,我没有把采用xx方法写下来,主要怕字描述 直接影响自己判断,感觉只要提到xx方法,然后依葫芦画瓢 ,就能解决。 就能把题目做出来完全不是这样的。因为这事后总结的,事前怎么想的谁知道呢

举一反三

单词拆分 II

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Offer多多 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目描述
  • 想法1
  • 想法2
  • 想法3
  • 扩展总结
  • 举一反三
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档