单词拆分(动态规划)
1. 题目描述
难度:中等
2. 题目分析
这道题的难点在于示例3,字符串包含字典中的所有单词,但是就是无法由字典中的单词组成,要么就是多个字符,要么就是少个字符,所以在使用动态规划方法的时候要考虑这种情况的发生。
- 动态规划
我们先考虑这样一个子问题:前i个子串是否能够被字典中的单词组成,如果可以的话,我们就在这个子串的位置做一个标记,也就是将i+1记录下来,然后我们在判断第i+1到第j位的子串时候能够被匹配,如果到最后一位的子串能够被匹配,那么我们就可以判断该字符串是可以被匹配的。以示例3为例:
我们定义一个数组label来存放能够匹配的位置信息,长度与s字符串的长度相等。
-
第一步:我们将label中的第一个元素初始化为0,也就是我们要从0开始进行判断
-
第二步:不能匹配
-
第三步:可以匹配,我们将该下标的后一位也就是3存放在label中,以便进行第二轮的判断
-
第四步:可以匹配,我们将4存放在下标中。
-
第五步:到这一步的时候,我们不仅要判断0-4之间的子串“CATSA”是否满足要求,也要判断3-4之间的子串“SA”是否满足要求,如果满足,将下标的后一位放入label数组,否则继续后移。
-
第六步:当移动到 i=6 的时候,我们的判断步骤为:
- 0-6的子串“CATSAND”不匹配;
- 3-6的子串“SAND”匹配;
- 4-6的子串“AND”匹配
只要出现匹配的情况(一次或者多次),我们都要下标 i+1存入label数组中。
-
第七步:当移动到最后一位时,我们的判断步骤为:
- 0-8的子串“CATSANDOG”不匹配;
- 3-8的子串“SANDOG”不匹配;
- 4-8的子串“ANDOG”不匹配
- 7-8的子串“OG”不匹配
所以最终我们得到的label数组为:
当最后一位不匹配的情况出现时,我们就可以判定该字符串是不满足条件的,所以我们可以通过判断label中数组的最后一位是否和字符串的长度相等来得出结论。
3. C语言实现
代码如下:
// 判断字符片段是否在wordDict里面
bool match(char *s, int left, int right, char ** wordDict, int wordDictSize){
int lens = right - left + 1;
int i = 0, j = 0;
for(i = 0; i < wordDictSize; i++){
if(lens == strlen(wordDict[i])){
// 如果长度与字典中的长度一致,就判断该片段是否和字典中单词一样
j = 0;
while(s[left+j] == wordDict[i][j]){
j++;
if(j==lens) return 1;
}
}
}
return 0;
}
bool wordBreak(char * s, char ** wordDict, int wordDictSize){
int lens = strlen(s); // 字符串的长度
int* label = (int *)malloc(sizeof(int)*(lens+1)); //label记录能够匹配字符片段的下标
int index = 0;
bool res;
label[index] = 0;
if(lens == 1) return match(s, 0, 0, wordDict, wordDictSize);
for(int i = 1; i <= lens; i++){
for(int j = 0; j <= index; j++){
// 如果子串与字典匹配,就记录该坐标并退出该循环
if(match(s, label[j], i-1, wordDict, wordDictSize)){
index++;
label[index] = i;
break;
}
}
}
res = label[index] == lens;
free(label); // 释放内存
return res;
}
运行结果为:
4. Python实现
代码如下:
class Solution:
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
if not s:
return True
breakp = [0]
for i in range(len(s) + 1):
for j in breakp:
if s[j:i] in wordDict:
breakp.append(i)
break
return breakp[-1] == len(s)
运行结果为: