leetcode笔记—Substring with Concatenation of All Words加最小的窗户子串

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(needToFindhasFound),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);  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值