(算法)最⼩覆盖⼦串——<滑动窗⼝+哈希表>

1. 题⽬链接:76.最⼩覆盖⼦串

2. 题⽬描述:

 3. 解法(滑动窗⼝+哈希表):

算法思路:

◦ 研究对象是连续的区间,因此可以尝试使⽤滑动窗⼝的思想来解决。

◦ 如何判断当前窗⼝内的所有字符是符合要求的呢?

        我们可以使⽤两个哈希表,其中⼀个将⽬标串的信息统计起来,另⼀个哈希表动态的维护窗⼝ 内字符串的信息。

        当动态哈希表中包含⽬标串中所有的字符,并且对应的个数都不⼩于⽬标串的哈希表中各个字 符的个数,那么当前的窗⼝就是⼀种可⾏的⽅案。

算法流程:

a. 定义两个全局的哈希表: 1 号哈希表hash1 ⽤来记录⼦串的信息, 2 号哈希表hash2 ⽤来记录⽬标串t 的信息;

b. 实现⼀个接⼝函数,判断当前窗⼝是否满⾜要求:

        i. 遍历两个哈希表中对应位置的元素:

                • 如果t 中某个字符的数量⼤于窗⼝中字符的数量,也就是2 号哈希表某个位置⼤于 1 号哈希表。说明不匹配,返回false ; 

                • 如果全都匹配,返回true 。

主函数中: 

a. 先将t 的信息放⼊2 号哈希表中; 

b. 初始化⼀些变量:左右指针: left = 0,right = 0 ;⽬标⼦串的⻓度: len = INT_MAX ;⽬标⼦串的起始位置: retleft ;(通过⽬标⼦串的起始位置和⻓度,我们就 能找到结果)

c. 当right ⼩于字符串s 的⻓度时,⼀直下列循环: 

        i. 将当前遍历到的元素扔进1 号哈希表中; 

        ii. 检测当前窗⼝是否满⾜条件:

                • 如果满⾜条件:

                        ◦ 判断当前窗⼝是否变⼩。如果变⼩:更新⻓度len ,以及字符串的起始位置 retleft ;

                        ◦ 判断完毕后,将左侧元素滑出窗⼝,顺便更新1 号哈希表;

                        ◦ 重复上⾯两个过程,直到窗⼝不满⾜条件;

        iii. right++ ,遍历下⼀个元素; 

d. 判断len 的⻓度是否等于INT_MAX : 

        i. 如果相等,说明没有匹配,返回空串;

        ii. 如果不想等,说明匹配,返回s 中从retleft 位置往后len ⻓度的字符串。

C++算法代码: 

class Solution
{
public:
    string minWindow(string s, string t)
    {
        int kind = 0; //记录字符串s中的元素种类
        unordered_map<char, int>hash1;   //子字符串中元素的出现次数
        for (int i = 0; i < t.size(); i++)
        {
            if (hash1[t[i]]++ == 0)
            {
                kind++;
            }
        }
        unordered_map<char, int>hash2;   //查找字符串中元素的出现次数
        //滑动窗口
        int count = 0;    //记录满足字符串s的元素个数
        int begin;  //答案的起始位置
        int key_minsize = INT_MAX;   //答案的最短长度
        bool value = false; //判断是否有答案
        for (int left = 0, right = 0; right < s.size(); right++)
        {
            //进窗口
            hash2[s[right]]++;
            //判断
            //如果出现次数要比子串只多不少,满足则计数器加1
            if (hash1[s[right]]&&hash2[s[right]] == hash1[s[right]])
            {
                count++;
            }
            //出窗口
            while (count == kind)
            {
                if (key_minsize > right - left + 1)
                {
                    value = true;
                    begin = left;
                    key_minsize = right - left + 1;
                    
                }
                //当退窗口的字母在子串中存在,且满足条件则计数器减1
                if (hash1[s[left]] && hash2[s[left]] == hash1[s[left]])
                {
                    count--;
                }
                hash2[s[left]]--;
                left++;
            }
        }
        if (value)
        {
            return s.substr(begin,key_minsize);
        }
        else
        {
            return "";
        }
    }
};

 Java算法代码:

class Solution {
	public String minWindow(String ss, String tt) {
		char[] s = ss.toCharArray();
		char[] t = tt.toCharArray();
		int[] hash1 = new int[128]; // 统计字符串 t 中每⼀个字符的频次 
		int kinds = 0; // 统计有效字符有多少种 
		for (char ch : t)
			if (hash1[ch]++ == 0) kinds++;
		int[] hash2 = new int[128]; // 统计窗⼝内每个字符的频次 
		int minlen = Integer.MAX_VALUE, begin = -1;
		for (int left = 0, right = 0, count = 0; right < s.length; right++)
		{
			char in = s[right];
			if (++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count 
			while (count == kinds) // 判断条件 
			{
				if (right - left + 1 < minlen) // 更新结果 
				{
					minlen = right - left + 1;
					begin = left;
				}
				char out = s[left++];
				if (hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count 
			}
		}
		if (begin == -1) return new String();
		else return ss.substring(begin, begin + minlen);
	}
}
  • 26
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

课堂随笔

感谢支持~~~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值