【算法百题之五十九】串联所有单词的子串
大家好,我是Lampard~~
很高兴又能和大家见面了,接下来准备系列更新的是算法题,一日一练,早日升仙!
今天的问题是:串联所有单词的子串
示例:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
思路:最直接的思路,判断每个子串是否符合,符合就把下标保存起来,最后返回即可。
然后我们要做的事情就可以分成两个:其一是如何拆分出各个子串,其二是如何判断是否符合。我们先讲第一个,从字符串s中拆分出子串,我们只需要得到words中的字符串拼接起来的目标字符串长度strLength,然后利用string的函数string.strsub(起始位置i,字符串长度strLength)就可以了。第二个问题比较难解决,就是怎么判断出子串是否就是我们想要的
判断是否自己想要的子串:
由于子串包含的单词顺序并不需要固定,如果是两个单词 A,B,我们只需要判断子串是否是 AB 或者 BA 即可。如果是三个单词 A,B,C 也还好,只需要判断子串是否是 ABC,或者 ACB,BAC,BCA,CAB,CBA 就可以了,但如果更多单词呢?那就崩溃了。
用两个 HashMap 来解决。首先,我们把所有的单词存到 HashMap 里,key 直接存单词,value 存单词出现的个数(因为给出的单词可能会有重复的,所以可能是 1 或 2 或者其他)。然后扫描子串的单词,如果当前扫描的单词在之前的 HashMap 中,就把该单词存到新的 HashMap 中,并判断新的 HashMap 中该单词的 value 是不是大于之前的 HashMap 该单词的 value ,如果大了,就代表该子串不是我们要找的,接着判断下一个子串就可以了。如果不大于,那么我们接着判断下一个单词的情况。子串扫描结束,如果子串的全部单词都符合,那么该子串就是我们找的其中一个。
算法代码:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ret;
if (words.size() == 0) {
// 判断words为空,因为下面用到了words[0]
return ret;
}
int wordLength = words[0].length();
int strLength = 0;
int wordNum = words.size();
map<string, int> m1;
for (int i = 0; i < wordNum; i++) {
// 把每一子串重复出现的次数记录下来
m1[words[i]]++;
strLength += words[i].length();
}
if (s.length() < strLength) {
return ret;
}
map<string, int> m2;
string curStr;
for (int i = 0; i <= s.length() - strLength; i++) {
curStr = s.substr(i, strLength);
while (!curStr.empty()) {
string word = curStr.substr(0, wordLength);
if (m1.count(word) == 0) {
break;
}
m2[word]++;
if (m2[word] > m1[word]) {
break;
}
if (curStr.length() == wordLength) {
curStr = "";
ret.push_back(i);
}
else{
curStr = curStr.substr(wordLength, strLength);
}
}
m2.clear();
}
return ret;
}
要注意一开始输入的就是不合法的情况,考虑程序的健壮性。比如目标字符串比字符串s还长,那就直接返回。
测试结果: