[leetcode] 139. Word Break [python]


单纯的想记录下这个问题,解决整个问题的思路。如果想要快速的看,就直接看dp的或者1.2 带有记忆的递归部分.

问题定义

leetcode 139 : 是否给定的句子s能够被拆分成字典wordDict里面的内容。如下例子 leetcode能被拆分为leetcode,因此,结果返回为True

s = "leetcode", wordDict = ["leet", "code"]

1. 基于递归的思路

1.1 暴力递归

这是最能够想到的方法,整体的思路为:如果s是以wordDict中的任意一个单词开头的,只要保证s[len(w):]之后满足可以被拆分,那么么整个s就是可以被拆分的。基于这个思想,有如下代码:

def wordBreak1(self, s, wordDict):
	# dfs 暴力:毫无疑问超时 T_T
	if not s or s in wordDict:
		return True
	for w in wordDict:
		if s.startswith(w): # 如果s是以wordDict中的任意一个单词开头的
			if self.wordBreak(s[len(w):],wordDict): # 只要保证s[len(w):]之后满足可以被拆分
				return True
	return False

1.2 带有记忆力的递归

显然,上面的超时了…,fine,我们想别的办法。我们给它个记忆。带有记忆的递归实际分为两种,一种可以将memo[s]用来记住访问过的字符串是否可以被拆分,另外一种memo[i]用来记住范围为[0, i)的子字符串是否可以拆分或者是[i,len(s))的字符串是否可以背拆分。基于这两种思路,我们有以下的做法。

  • memo[s]: 访问过的字符串s是否可以被拆分
def wordBreak2(self, s, wordDict):
	# 定义长度为len(s) memo[i]定义为范围为[0, i)的子字符串是否可以拆分,
	if not s or not wordDict:
		return False
	memo = {'':True} # 因为面可能会有访问s[len(s):],说明前面的s已经可分了,因此其为True

	def dfs_memo(s):
		if s in memo: return memo[s]
		if s in wordDict:
			memo[s] = True
			return True
		for w in wordDict:
			if s.startswith(w):
				if dfs_memo(s[len(w):]):  # 这里理解下memo = {'':True} 
					return True
				else:
					memo[s[len(w)]] = False

		memo[s] = False
		return False

	return dfs_memo(s)

在这里插入图片描述

  • memo[i]:(或许上面的幅图可以帮助理解~)

    • dp[i]:从开始位置0到i即s[:i]是可分的
    def wordBreak3(self, s, wordDict):
        # 定义长度为len(s) memo[i]定义为范围为[0, i)的子字符串是否可以拆分,
        if not s or not wordDict:
            return False
        memo = {}
    
        def dfs_memo(start):
            if start in memo: return memo[start]
            if s[start:] in wordDict:
                memo[start] = True
                return True
            for w in wordDict:
                if s[start:].startswith(w):
                    if dfs_memo(start+len(w)):
                        return True
                    else:
                        memo[start] = False
    
            memo[start] = False
            return False
    
        return dfs_memo(0)
    
    • dp[i]:从位置i到s结束即s[i:]是可分的
    def wordBreak4(self, s, wordDict):
        # 定义长度为len(s) memo[i]定义为范围为[i:]的子字符串是否可以拆分,(意味着前面的是可以拆分的)
        if not s or not wordDict:
            return False
        memo = {}
    
        def dfs_memo(end):
            if end in memo: return memo[end]
            if s[:end] in wordDict: # 注意这是是从头0到end在wordDict种
                memo[end] = True
                return True
            for w in wordDict:
                if s[:end].endswith(w): # 如果s[:end] 是以w结束的,那么才可以将start往前移动,注意这离的 endswith
                    if dfs_memo(end-len(w)):
                        return True
                    else:
                        memo[end] = False
    
            memo[end] = False
            return False
        return dfs_memo(len(s)) # 注意开始的位置
    

2. 动态规划

写这个题目参考这位大佬的文章思考的,里面有很多总结挺不错,(小菜鸟不知正确与否,感觉是很有道理的)

这种使用记忆数组memo的递归写法,和使用dp数组的迭代写法,乃解题的两大神器,凡是能用dp解的题,一般也有用记忆数组的递归解法。

对于本题:dp[i]表示范围[0, i)内的子串可以被拆分,注意这里dp数组的长度比s串的长度大1,是因为我们要handle空串的情况,我们初始化dp[0]为true,然后开始遍历。dp[i]为True,且s[i:j] in wordDict,可以将dp[j]标记为True

def wordBreak_dp2(self, s, wordDict):
	dp = [False] * (len(s)+1)
	dp[0] = True
	for i in range(len(s)):
		if dp[i]:
			for j in range(i,len(s)):  # 可以从dp的角度想以len(s)作为结束点
				if s[i:j+1] in wordDict:
					dp[j+1] = True
	return dp[-1]

3. BFS

…emmmmm…还未看?…可以去那位大佬或者leetcode上看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值