窗口有关算法题目

窗口算法

窗口算法是我自己起的不严谨的名字,因为最近做了几个leetcode中几个算法题目,发现其算法的都需要通过维护一个窗口来实现,说到窗口,我们肯定会想到TCP/IP中的滑动窗口协议,其实这类算法题目和这个有点神似的。

滑动窗口协议是用来改善吞吐量的一种技术,TCP中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。——百度百科

现在我汇总一下,分析一下这种类型题目的思路。


最长无重复字符的子串

该题目原文是:“Given a string, find the length of the longest substring without repeating characters.”

思路很简单,维护一个窗口,窗口的大小表示当前的无重复的字符串的长度,用一个变量来记录窗口的起始位置,遍历字符串的字符,遇到一个字符,就需要判断该字符串是否出现过

  • 没出现过,那么直接记录该字符在字符串中索引
  • 出现过,判断上一次该字符出现的位置;该位置加1后与窗口的起始位置比较,取两者中的最大值作为新窗口的起始位置。

然后每次计算现在窗口的大小,不断更新窗口的历史最大值。代码如下:

int lengthOfLongestSubstring(string s) 
{
    vector<int> charIndex(256, -1);
    int longest = 0, m = 0;

    for (int i = 0; i < s.length(); i++) {
        m = max(charIndex[s[i]] + 1, m);    // 确定窗口的起始位置
        charIndex[s[i]] = i;
        longest = max(longest, i - m + 1);
    }

    return longest;
}

最小窗口子串

题目原文是:“Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).”就是要求S中包含T的所有的字符的最短子串的长度。

思路的核心也是窗口,把所有包含T中字符的窗口(子串)大小求出来,取其中的最小值就可以。如何求得窗口范围,这个题目稍微麻烦了些。

我们需要把T中出现的字符存到一个hashmap中(C++11提供了unordered_map),以便我们快速访问。由于此题目的特殊性都是字符,我们可以提供一个大小为256的数组来充当hashmap.

  • 然后从S的字符串的开头开始遍历,尾指针不断往后扫,遇到一个字符,判断是否在T中出现过,若出席过,count++,直到count==T.size().
  • 这时候然收缩头指针,直到不能再收缩为止,此时找到该段包含T中所有字符的最小窗口。之后继续尾指针继续后扫,遍历S串的下一个字符,重复以上的步骤,直到结束。过程中保存历史最小窗口。
    代码参考了一下网上的,如下:
string minWindow(string S, string T) {
        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++)
        {
            //T中没有出现的字符
            if(needFind[S[end]]==0) continue;

            hasFind[S[end]]++;
            //若是超过了T中字符出现的次数,就不需要count++
            if(hasFind[S[end]] <= needFind[S[end]])
                count++;

            //a window exists from begin to end
            if(count==tLen)
            {
                //不断移动头指针来找到最小窗口
                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);
    }

我记得还有一个类似的题目,但是想不起来了。
总的来说这类题目需要我们维护一个窗口,可以通过记录窗口的大小,头指针,尾指针来维护,而且一般窗口中包含的元素需要满足题意中某些条件,一般来说就是包含了某某的全部的元素。
这类题目不难,关键在于想到如何来不断调整窗口的大小,头尾指针,想明白了这点,问题就基本可以解决哈。

发布了42 篇原创文章 · 获赞 32 · 访问量 8万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章