139. 单词拆分
https://leetcode-cn.com/problems/word-break/
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
# dp[i]表示s[:i]能否被拆分
# 如何计算dp[i]:枚举以s[i-1]结尾的字符串片段s[j:i]
# dp[i] = dp[j] && s[j:i] in wordDict
# 初始化dp[0]=True。最终返回dp[n]
if not wordDict:
return False
n = len(s)
dp = [False for _ in range(n+1)]
dp[0] = True
# set的搜索只需要O(1)的时间复杂度,而list需要O(n)
word_set = set(wordDict)
# 单词的最大长度
max_len = max(len(word) for word in word_set)
for i in range(1, n+1):
# s[j:i]的长度应小于或等于单词的最大长度
board = max(0, i - max_len)
j = i - 1
while j >= board:
if dp[j] and (s[j:i] in word_set):
dp[i] = True
break
j -= 1
return dp[n]
时间复杂度:O(n^2)。需要枚举n个状态,每个状态需要枚举n个分割点 j,set的搜索需要O(1)的时间复杂度,因此总的时间复杂度为O(n^2)
空间复杂度:O(n)
140. 单词拆分 II
https://leetcode-cn.com/problems/word-break-ii/
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: List[str]
"""
if not wordDict:
return []
# dp[i]记录所有以s[i-1]结尾的单词起始位置
# 如何计算dp[i]:枚举以s[i-1]结尾的字符串片段s[j:i]
# if dp[j] && s[j:i] in wordDict, dp[i].append(j)
# 然后从后往前深度优先遍历
n = len(s)
dp = [[] for _ in range(n + 1)]
# dp[0]=True。考虑匹配到第一个单词s[0:i]时,dp[i+1]=[0]
dp[0] = True
# 单词的最大长度,往前找到这个长度还找不到就不需要再找下去了
max_len = max(len(word) for word in wordDict)
# set的搜索只需要O(1)的时间复杂度,而list需要O(n)
word_set = set(wordDict)
for i in range(1, n + 1):
# 注意下界的取值,下界是取不到的,所以要-1
for j in range(i - 1, max(0, i - max_len) - 1, -1):
# 找到所有以s[i-1]结尾的单词起始位置
if s[j:i] in word_set and dp[j]:
dp[i].append(j)
words_list = []
# 当前路径
words = []
def dfs(i):
# 一条能够全部拆分的路径最终会取到dp[0]
if i == 0:
words_list.append(words[:])
return
for j in dp[i]:
# 将单词取出
words.append(s[j:i])
dfs(j)
# 回溯
words.pop()
dfs(n)
result = []
for words in words_list:
result.append(" ".join(word for word in words[::-1]))
return result
本题的时间与空间复杂度均为指数级别,较难进行具体的分析