【LeetCode & 剑指offer刷题】动态规划与贪婪法题15:Word Break(系列)
【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)
Word Break(系列)
Word Break
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 a ssume the dictionary does not contain duplicate words.
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
C++
/*
问题:拆分词句,看给定的词句能分被拆分成字典里面的内容
方法:动态规划
f[i]
表示s[0,i-1]是否可以被分词
,表示在第i个字符后面的隔板
状态转移方程:f(i) = any_of(f(j)&&s[j,i-1] ∈ dict); j = 0~i-1
例:
Input: s = "leetcode", wordDict = ["leet", "code"]
f[0] = true
i=1,j=0: l
i=2,j=0: le
j=1: e
i=3,j=0: lee
j=1: ee
j=2: e
i=4,j=0: leet f[4] = true
j=1: eet
j=2: et
j=3: t
...
O(n^2)
假设总共有n个字符串,并且字典是用HashSet来维护,那么总共需要n次迭代,每次迭代需要一个取子串的O(i)操作,然后检测i个子串,而检测是constant操作。所以总的时间复杂度是O(n^2)(i的累加仍然是n^2量级),而空间复杂度则是字符串的数量,即O(n)(本题还需加上字典的空间)
*/
class
Solution
{
public
:
bool
wordBreak
(
string s
,
vector
<
string
>
&
wordDict
)
{
unordered_set
<
string
>
new_dict
(
wordDict
.
begin
(),
wordDict
.
end
());
//转化为哈希表,方便查找
//长度为n的字符串有n+1个隔板,多分配一个空间以方便后续递推
vector
<
bool
>
f
(
s.size() + 1
,
false
);
f
[
0
]
=
true
;
// 空字符串,初始化为true,以便后续迭代
for
(
int
i
=
1
;
i
<
f
.
size
();
i
++)
//以s[i-1]字符结尾的子串, i=1~n
{
for
(
int
j
=
0
;
j
<
i
;
j
++)
//以s[j]开头的子串,j=0~i-1
{
if (f[j] &&
new_dict
.
find
(
s
.
substr
(
j
,
i
-
j
))
!=
new_dict
.
end
())
//substr[start,len)
{
f
[i] = true;
break
;
}
}
}
return
f[s.size()];
}
};
Word Break II
Given a
non-empty
string
s
and a dictionary
wordDict
containing a list of
non-empty
words, add spaces in
s
to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
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.
Example 1:
Input:
s = "
catsanddog
"
wordDict =
["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
Example 2:
Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
"pine apple pen apple",
"pineapple pen apple",
"pine applepen apple"
]
Explanation:
Note that you are allowed to reuse a dictionary word.
Example 3:
Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]
C++
/*
问题:拆分词句2,返回所有分隔结果
方法:dfs
联系排列组合问题
这里用一个hash表避免对相同子串s进行重复分隔,减少重复计算
*/
class
Solution
{
public
:
vector
<
string
>
wordBreak
(
string s
,
vector
<
string
>&
wordDict
)
{
unordered_map
<
string
,
vector
<
string
>>
m
;
return
dfs
(
s
,
wordDict
,
m
);
}
vector
<
string
>
dfs
(
string s
,
vector
<
string
>&
wordDict
,
unordered_map
<
string
,
vector
<
string
>>&
m
)
{
if
(
m
.
find
(
s
)
!=
m
.
end
())
return
m
[
s
];
//如果对s的分隔已经递归过了,就直接退出
if
(
s
.
empty
())
return
{
""
};
//map型数据类型用{},递归的出口
vector
<
string
>
res
;
//某一次的分隔结果
for
(
string word
:
wordDict
)
//遍历字典中的单词(递归的分支)
{
if
(
word
==
s
.
substr
(
0
,
word
.
size
())
)
//如果当前单词在s开头
{
//substr 返回子串 [pos, pos+count) 。若请求的子串越过 string 的结尾,或若 count == npos ,则返回的子串为 [pos, size())
vector
<
string
>
rem
=
dfs
(
s
.
substr
(
word
.
size
()),
wordDict
,
m
);
//对该单词后面的子串递归(分支的深度),返回后面子串的分隔结果
for
(
string str
:
rem
)
//拼接后面子串的分隔结果与当前单词
{
res
.
push_back
(
word
+
(
str
.
empty
()
?
""
:
" "
)
+
str
);
//将word和str push到结果向量中,中间用空格隔开,此为某一种结果
}
}
}
return
m
[
s
]
=
res
;
//返回对s的分隔结果
}
};