Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s = "leetcode"
,
dict = ["leet", "code"]
.
Return true because "leetcode"
can be segmented as "leet code"
.
判断给定字符串是否可以用空格分割为字典中的单词。
一、递归求解: Time Limit Exceeded
最容易想到的是递归求解了,不过也很容易想到会超时:
class Solution {
public:
bool wordBreak(string s, unordered_set<string> &dict) {
if (dict.empty()) {
return false;
}
if (dict.find(s) != dict.end()) {
return true;
}
auto sz = s.size();
for (auto i = 1; i < sz; ++i) {
if (wordBreak(s.substr(0,i), dict)
&& wordBreak(s.substr(i,sz-i), dict)) {
return true;
}
}
return false;
}
};
二、动态规划:记忆法
分析上面的递归解法可知有大量的重叠子问题,可用记忆法记录字符串中的子串[i,j)是否可被分割为字典中的单词。我用了一个固定大小的二维数组t,t[i][j]表示的是起始位置i,长度为j的字串是否可分割,0表示该字串还没有查询过,1表示可以分割,-1表示不可以分割。定义了一个成员变量s用来保存要查询的字符串。
class Solution {
public:
Solution() {
memset(t, 0, sizeof(t));
for (int i = 0; i < MAX_SIZE; ++i)
t[i][0] = 1;
}
bool wordBreak(string s, unordered_set<string> &dict) {
if (dict.empty() || s.empty()) {
return false;
}
this->s = s;
return wordBreakImpl(0,s.size(),dict);
}
bool wordBreakImpl(int pos, int len, unordered_set<string> &dict) {
if (0 == t[pos][len]) {
t[pos][len] = -1;
for (int i = 1; i <= len; ++i) {
if (exists(pos,i,dict) && wordBreakImpl(pos+i,len-i,dict)) {
t[pos][len] = 1;
break;
}
}
}
return t[pos][len] == 1;
}
bool exists(int pos, int len, unordered_set<string> &dict) {
return dict.find(s.substr(pos,len)) != dict.end();
}
private:
const static int MAX_SIZE = 200;
int t[MAX_SIZE][MAX_SIZE];
string s;
};