1、题意:给定一个字符串S和一个字符串数组L,L中的字符串长度都相等,找出S中所有的子串恰好包含L中所有字符各一次,返回子串的起始位置。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if(words.size() == 0) return vector<int>(); //words为空,返回空
int wordLen = words[0].size(); //words中每个单词的长度
int wordNum = words.size(); // words中单词的个数
int wordsLen = wordLen*wordNum; //words中字母的个数
vector<int> ans; //返回向量容器
map<string, int> wordMap;
for(int i = 0; i < words.size(); ++i)
{
//统计words中每个单词出现的次数
if(wordMap.find(words[i]) == wordMap.end()) //map中没有words[i]
wordMap[words[i]] = 1;
else wordMap[words[i]]++;
}
for(int i = 0; i < s.size(); ++i)
{
int j = wordsLen-1+i;
if(j < s.size())
{
map<string, int> curWordMap;
bool flag = true;
for(int k = i, curWordNum = 0; curWordNum < wordNum; k += wordLen, curWordNum++)
{
string tmpStr = s.substr(k, wordLen); //取一个单词长度的字符串
if(curWordMap.find(tmpStr) == curWordMap.end()) curWordMap[tmpStr] = 1; //curwordmap中没有的话
else curWordMap[tmpStr]++;
if(wordMap.find(tmpStr) == wordMap.end()|| curWordMap[tmpStr] > wordMap[tmpStr]) //wordmap中没有该单词||数量多于wordmap中该单词的数量
{
flag = false;
break;
}
}
if(flag) ans.push_back(i);
}
}
return ans;
}
};
2、双指针,动态维护一个区间。尾指针不断往后扫,当扫到有一个窗口包含了所有T的字符后,然后再收缩头指针,直到不能再收缩为止。最后记录所有可能的情况中窗口最小的
S = "ADOBECODEBANC"
T = "ABC"
Minimum window is "BANC"
.
思路:
我们下面使用 S = "acbbaca" and T = "aba" 来演示这个算法。基本思路是在遍历S的过程中,使用两个指针(合法window的begin和end索引)和两table(needToFind和hasFound),needToFind保存T中每个字母的个数(二娃:相当于我们的needToFill),hasFound保存当前收集到的每个字母的个数。我们也同时使用一个count变量,用来保存当前收集到的字母总数,但是那些收集过了的字母数并不算在count里面。这样的话,当count等于T.length,那我们就知道遇到一个合法的window了。
我们利用end指针来遍历S,假设当前end指向S中的字母x,如果x是T中的字母,hasFound[x]加一。如果hasFound[x]目前还小于等于needToFind[x](二娃:说明字母x还没有收集全或者刚刚收集全哦),那么我们同时也增加count。当合法window的条件满足,也就是count等于T.length,我们立即递增begin指针,并且保证在递增的过程中始终有count等于T.length。
在递增begin指针的过程中,我们怎么样才能保证“始终有count等于T.length”呢?
假设begin指向字母x,如果hasFound[x]大于了needToFind[x],hasFound[x]减去一,同时递增begin。(二娃:这里很有画面感哦。因为当前遇到的x是冗余的靠左的字母,这里的操作其实等价于前面两个算法中的“删除charAppearanceRecorder中相应的字母的链表头节点”,有点儿像一个是lazy去重,一个是eager去重)否则的话,当前的begin就是window的起始索引了。
接下来我们就可以通过end - begin + 1得到当前window的长度了。这里便可以更新最小window长度。
算法实际上首先找到第一个合法的window,然后在接下来的扫描过程中保持window的合法性(二娃:其实就是count 始终小于等于(当遇到新window))
code1、
public String minWindow3(String S, String T){
2 HashMap<Character, Integer> needToFill = new HashMap<Character, Integer>();
3 HashMap<Character, Integer> hasFound = new HashMap<Character, Integer>();
4 int count = 0;
5 for(int i = 0; i < T.length(); i++){
6 if(!needToFill.containsKey(T.charAt(i))){
7 needToFill.put(T.charAt(i), 1);
8 hasFound.put(T.charAt(i), 0);
9 }else {
10 needToFill.put(T.charAt(i), needToFill.get(T.charAt(i)) + 1);
11 }
12 }
13 int minWinBegin = -1;
14 int minWinEnd = S.length();
15 for(int begin = 0, end = 0; end < S.length(); end++){
16 char c = S.charAt(end);
17 if(needToFill.containsKey(c)){
18 hasFound.put(c, hasFound.get(c) + 1);
19 if(hasFound.get(c) <= needToFill.get(c)){
20 count++;
21 }
22 if(count == T.length()){
23 while(!needToFill.containsKey(S.charAt(begin)) ||
24 hasFound.get(S.charAt(begin)) > needToFill.get(S.charAt(begin))) {
25 if(needToFill.containsKey(S.charAt(begin))
26 && hasFound.get(S.charAt(begin)) > needToFill.get(S.charAt(begin))){
27 hasFound.put(S.charAt(begin), hasFound.get(S.charAt(begin)) - 1);
28 }
29 begin++;
30 }
31 if(end - begin < minWinEnd - minWinBegin){
32 minWinEnd = end;
33 minWinBegin = begin;
34 }
35 }
36 }
37 }
38 return minWinBegin == -1 ? "" : S.substring(minWinBegin, minWinEnd + 1);
code2、
int sLen=S.size(); //大字符串长度
int tLen=T.size(); //小字符串长度
if(tLen==0 || sLen<tLen) return "";
int needFind[256]={0};
int hasFind[256]={0};
for(int i=0;i<tLen;i++)
{
needFind[T[i]]++;
}
int minWindowLength=INT_MAX;
int minBegin=0;
int minEnd=sLen-1;
int begin=0;
int end=0;
for(int count=0;end<sLen;end++)
{
//skip those not in T
if(needFind[S[end]]==0) continue; //
hasFind[S[end]]++;
//update the total number of characters found(aaa only counts 2 for aa in T)
if(hasFind[S[end]] <= needFind[S[end]])
count++;
//a window exists from begin to end
if(count==tLen)
{
//move begin pointer to find the minimum window
while(begin<end)
{
if(needFind[S[begin]]==0)
{
begin++;
continue;
}
if(hasFind[S[begin]] > needFind[S[begin]])
{
hasFind[S[begin]]--;
begin++;
continue;
}
else
break;
}
int tmpWindowLength=end-begin+1;
if(tmpWindowLength < minWindowLength)
{
minBegin=begin;
minEnd=end;
minWindowLength=tmpWindowLength;
}
}
}
if(minWindowLength==INT_MAX)
return "";
return S.substr(minBegin,minWindowLength);