给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
解题思路
第一种方法:DFS 搜索
搜索遍历所有的子串,看子串是否在单词表中,如果该单词在单词表中并且剩余的子串也全部符合条件,那么整体的串就符合条件。
第二种方法: dp所有的位置 如果之前的符合条件,并且剩余子串也在单词表中,那么就记下该位置为true,最后返回最后位置的dp值。
代码
public static boolean wordBreak(String s, List<String> wordDict) {
List<String> res = new ArrayList<>();
return backTrace(s,res,0,s.length(),wordDict,new ArrayList<>());
}
private static boolean backTrace(String cs, List<String> res, int begin, int length, List<String> wordDict,List<Integer> checkedin) {
if(begin == length){
return true;
}
for(int i=begin+1;i<=length;i++){
if(checkedin.contains(i))continue;
if(wordDict.contains(cs.substring(begin,i))){
if (backTrace(cs, res, i, length,wordDict,checkedin))return true;
//记录下从i开始判断的时候失败的情况,以免之后再重复计算
checkedin.add(i);
}
}
return false;
}
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int len = s.length();
int maxWordLength = 0;
//这一步是说:对于检查一个字符串是否出现在给定的字符串列表里一般可以考虑哈希表来快速判断。(相对于list结构set的查询效率更高,但是是不是在创建这个set的时候也浪费了一些时间呢)
Set<String> wordSet = new HashSet<>(wordDict.size());
for (String word : wordDict) {
wordSet.add(word);
if (word.length() > maxWordLength) {
maxWordLength = word.length();
}
}
boolean dp [] = new boolean[s.length()+1];
dp[0] = true;
for(int i =1;i<len+1;i++){
//剪枝操作:如果i的位置大于了最大单词的长度,那么j的起始位置应该在 i减去最大长度的位置开始,因若不然s.substring(j,i)的长度是大于最大单词的 长度的,所以一定不可能匹配成功。
for(int j =(i < maxWordLength ? 0:i-maxWordLength);j<i;j++){
if(dp[j] && wordSet.contains(s.substring(j, i))){
dp[i] = true;
break;
}
}
}
return dp[len];
}
}