139 Word Break【逐一对比vs.多种 分割 组合】
片面思考的思路:
cpp
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
//字符串和对应的字典,如果s种可以用空格分隔出一个或多个字典里的词就返回true
//核心:按照字典里的词对s断句,对s遍历,如何判断s种有多余的字母是字典中不包含的?
//细节:字典里的词会被多次使用,且不按顺序出
//在这道题中,哈希表是怎么使用的?是存储字典中出现的单词个数?还是存储s中的单词出现的位置?
//如何确定单词范围?对dic也是一个一个比较吗?比较后怎么锁定s中的位置?
//单词范围由wordDic[i].size()确定,通过substr(pos,len)确定子串中的位置,哈希表用于存储开始位置和单词长度
//是哪个循环套哪个循环?
//因为一个字典会在s中多次出现且s的长度最大值才300所以Dic中嵌套s
//如何让s遍历的时候跳过已经判断出的范围?或者说如何判定s没有被排除完?
//s.replace(pos,len,'0')那这样就不需要哈希表了啊,直接最后遍历一遍是否全为0就好了
//unordered_map<int,int> StartAndLength;
int index = 0;
for(int i = 0 ; i < wordDict.size();i++){
int len = wordDict[i].size();
int index = 0;//每次都从头开始遍历
while(index < s.size()){
if(s[index] == wordDict[i][0] && s.substr(index,len) == wordDict[i]){
//StartAndLength[index] = len;
s.replace(index,len,string(len,' '));
index+=len;
}else{
index++;
}
}
}
for(char ch : s){
if(ch != ' '){
return false;
}
}
return true;
}
};
分析思路过程:
按照字典里的词对s断句,对s遍历,如何判断s种有多余的字母是字典中不包含的?
细节:字典里的词会被多次使用,且不按顺序出
在这道题中,哈希表是怎么使用的? 是存储字典中出现的单词个数?还是存储s中的单词出现的位置?-----》都是对中间态的记录,没有考虑哈希表的快速查找
如何确定单词范围? 对dic也是一个一个比较吗?比较后怎么锁定s中的位置?单词范围由wordDic[i].size()确定,通过substr(pos,len)确定子串中的位置,哈希表用于存储开始位置和单词长度-------》没有考虑多种组合的情况
//是哪个循环套哪个循环?
//因为一个字典会在s中多次出现且s的长度最大值才300所以Dic中嵌套s如何让s遍历的时候跳过已经判断出的范围?或者说如何判定s没有被排除完?
完全遍历一遍,所以主体还是对s的遍历
重新思考这道题目
按照难度来说,中等题也不太可能使用逐一对比+替换就能解决这个问题;
按照题型来说,这道题肯定需要用哈希表来解决
重新思考这道题,复盘哪些问题需要继续去思考的:
- 在这道题中,哈希表是怎么使用的?
- 如何确定单词范围?
- 如何判断s中有多余的字母是字典中不包含的?---->这个是多种组合?
第一个问题: 哈希表可以完成字典的快速查找 unordered_set,记录中间状态 unordered_map;
如果是前一种,也就是将wordDict变了一下,用于快速查找s子串种是否是字典中的单词hash.find(s.substr(pos,len)) != hash.end()就是查到了,这段子串是存在于hash中的。
【这种方式特别适合需要频繁查找字典的情况,因为 unordered_set 的查找操作具有平均 O(1) 的时间复杂度,非常高效。】
后一种的话,就是用哈希表记录检查过的字符串段,用于判断s已经被分割的字段,但这个不适合多种组合 的情况,类似于逐一对比,认准一个死理不改变,所以还是使用字典的快速查找。
第二个问题: 单词范围一种是遍历,确定s中的开始位置使用while和Dict中的单词逐一对比,已经被pass了;另一种是动态规划,动态规划尤其适用于需要检查多个可能分割点并且组合的情况,如字符串分割、路径问题等。
第三个问题: 实际上是回答怎么判定s是否可以被Dict中的单词们分割,结合第二个问题,使用动态规划划分单词范围,其中的dp[i]数组表示的是s中前i个字符是否可以被完全分割,如果在最终的dp数组中s.size()位置标记为false则表示无法分割。
cpp
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
//动态规划
//要做的事情是确定s中前i位是否可以被分割 dp[i]
vector<bool> dp(s.size()+1,false);
unordered_set<string> wordSet(wordDict.begin() , wordDict.end());
//初始条件dp[0] = true;
dp[0] = true;
//遍历string s找合适的切割组合
for(int i = 1 ; i <= s.size() ; i++){
//判定是否可以被分割
for(int j = 0 ; j < i ; j++){
//判定条件,关系式:wordSet.find(s.substr(j , i-j)) != wordSet.end()
if(dp[j] && wordSet.find(s.substr(j , i-j)) != wordSet.end()){
//可以被分割
dp[i] = true;
break;
}
}
}
return dp[s.size()];
}
};
review 467 Unique Substrings in Wraparound String
cpp
class Solution {
public:
int findSubstringInWraproundString(string s) {
//不同的非空的子串数目 不同+非空怎么判定-->记录数组dp 仅记录以26字母为结尾的
//要找的是 且是不同的 存在于base中的子串 所以判定dp[i]为以i = ch - 'a'为索引,以ch为结尾的最长子串
vector<int> dp(26,0);
int maxLen = 0;
//base的判定前者-后者为1 或者前者为z后者为a,记录长度
for(int i = 0 ; i < s.size() ; i++){
char ch = s[i];
if(i>0 && (s[i] - s[i-1] == 1 || (s[i-1] == 'z' && s[i] == 'a'))){
maxLen++;
}else{
//初始条件
maxLen = 1;
}
dp[ch-'a'] = std::max(maxLen,dp[ch-'a']);
}
int ans = 0;
for(int len : dp){
ans += len;
}
return ans;
}
};