139. Word Break

leetcode

Posted by chl on August 8, 2019

题目

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,和一个字典字符数组,判断该s字符串进行分割之后,分割的字符是否在字典中;字典中字符不重复,但是可以多次使用

例子

Example 1:

Input: s = “leetcode”, wordDict = [“leet”, “code”]
Output: true
Explanation: Return true because “leetcode” can be segmented as “leet code”.

Example 2:

Input: s = “applepenapple”, wordDict = [“apple”, “pen”]
Output: true
Explanation: Return true because “applepenapple” can be segmented as “apple pen apple”. Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
Output: false

思路

先将字典中的字符存入map中,然后从第一个字符开始匹配,匹配失败就取下一个字符组成新的单词再进行匹配,匹配成功之后从如果不为最后一个,则取下一个组成新的字符串,在进行匹配;在前面有匹配成功的情况下,如果匹配失败了,说明当前的分隔是错误的,需要回到上一次匹配成功的地方,不进行分割,接着组成新的字符串进行匹配;
使用递归的方法很快可以实现如下代码:

var Slen int
var DictMap map[string]int
func wordBreak(s string, wordDict []string) bool {
    if len(s)==0{
        return false
    }
    
    Slen = len(s)
    
    if len(wordDict)==0{
        return false
    }
    DictMap = make(map[string]int)
    for _,v := range wordDict{
        DictMap[v]=1
    }
    
    return Segmentation(s,0)
    
    
}

func Segmentation(s string,index int) bool{
    if index==Slen{
        return true
    }
    temps := ""
    for index<Slen{
        temps+=string(s[index])
        
        if _,ok := DictMap[temps];!ok{
            index++
            continue
        }
        index++
        if Segmentation(s,index){
            return true
        }
    }
    return false
}

但是这样会TL,仔细观察一下代码,会发现在递归的过程中会进行很多次重复的计算(如遍历到某个位置t时,发现以该位置为start的分隔往后都不匹配,那么会回溯到上一次成功的地方,在上一次成功的地方往后寻找,期间成功匹配之后,又进入到以t为start的情况,那么就会再一次计算,可以保持以start为起始的分隔情况),所以可以保存之前的计算结果,提高速率;
对以上代码进行更改如下:

var Slen int
var DictMap map[string]int
var Memo map[int]int //记忆数组
func wordBreak(s string, wordDict []string) bool {
    if len(s)==0{
        return false
    }
    
    Slen = len(s)
    
    if len(wordDict)==0{
        return false
    }
    DictMap = make(map[string]int)
    Memo = make(map[int]int)
    for i,_:=range s{
        Memo[i] = -1//初始为-1
    }
    
    for _,v := range wordDict{
        DictMap[v]=1
    }
    
    return Segmentation(s,0)
    
    
}

func Segmentation(s string,index int) bool{
    if index==Slen{
        return true
    }
    
    if Memo[index]==1{//如果为1,说明,以当前位置为头的分隔情况为true,直接返回
        return true
    }
    if Memo[index]==0{//如果为0,说明,以当前位置为头的分隔情况为false,直接返回
        return false
    }
    
    temps := ""
    start := index
    for index<Slen{
        temps+=string(s[index])
        
        if _,ok := DictMap[temps];!ok{
            index++
            continue
        }
        index++
        if Segmentation(s,index){
            Memo[start] = 1 //记录当前结果
            return true
        }
    }
    Memo[start] = 0//记录当前结果
    return false
}

总结

基本就是动态规划(dp)的题目了,可以通过以前的计算结果来减少重复计算,来找到最终的结果。本题关键点:记忆数组。