4.4.3 python 字符串双指针/哈希算法3 —— Minimum Window Substring & Smallest Range

这一节两道题颇有难度,hard层次的, 可谓需要对双指针和哈希二者理解十分透彻,当然了,一些特定的处理技巧也是很重要的。这两道题比较思想比较接近,建议认真研读,弄清这一类问题。

76. Minimum Window Substring

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).

Example:

Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"

Note:

  • If there is no such window in S that covers all characters in T, return the empty string "".
  • If there is such window, you are guaranteed that there will always be only one unique minimum window in S

题目解析:

典型的滑动窗问题,下面是其他大神的解法,附有注释,建议结合测试用例,print(need),观察need每一轮的变化(如下)来理解这一方法。

def minWindow(s, t):
    need = collections.Counter(t)            #hash table to store char frequency
    missing = len(t)                         #total number of chars we care
    start, end = 0, 0
    i = 0
    for j, char in enumerate(s, 1):          #index j from 1
        if need[char] > 0:
            missing -= 1
        need[char] -= 1
        if missing == 0:                     #match all chars
            while i < j and need[s[i]] < 0:  #remove chars to find the real start
                need[s[i]] += 1
                i += 1
            need[s[i]] += 1                  #make sure the first appearing char satisfies need[char]>0
            missing += 1                     #we missed this first char, so add missing by 1
            if end == 0 or j-i < end-start:  #update window
                start, end = i, j
            i += 1                           #update i to start+1 for next window
        # print(need)
    return s[start:end]

 

Counter({'B': 1, 'C': 1, 'A': 0})
Counter({'B': 1, 'C': 1, 'A': 0, 'D': -1})
Counter({'B': 1, 'C': 1, 'A': 0, 'D': -1, 'O': -1})
Counter({'C': 1, 'B': 0, 'A': 0, 'D': -1, 'O': -1})
Counter({'C': 1, 'B': 0, 'A': 0, 'D': -1, 'O': -1, 'E': -1})
Counter({'B': 0, 'C': 0, 'A': 0, 'D': -1, 'O': -1, 'E': -1})
Counter({'B': 0, 'C': 0, 'A': 0, 'D': -1, 'E': -1, 'O': -2})
Counter({'B': 0, 'C': 0, 'A': 0, 'E': -1, 'D': -2, 'O': -2})
Counter({'B': 0, 'C': 0, 'A': 0, 'D': -2, 'O': -2, 'E': -2})
Counter({'C': 0, 'A': 0, 'B': -1, 'D': -2, 'O': -2, 'E': -2})
Counter({'B': 0, 'C': 0, 'A': 0, 'D': -1, 'O': -1, 'E': -1})
Counter({'B': 0, 'C': 0, 'A': 0, 'D': -1, 'N': -1, 'O': -1, 'E': -1})
Counter({'D': 0, 'O': 0, 'B': 0, 'C': 0, 'A': 0, 'E': 0, 'N': -1})

然后也偷偷的放一个java的,毕竟性能更好(原谅我强迫症看耗时),思路略有不同,供大家继续学习。

public String minWindow(String S, String T) {
    if(S==null||S.isEmpty()||T==null||T.isEmpty()) return "";
    int i=0, j=0;
    int[] Tmap=new int[256];
    int[] Smap=new int[256];
    for(int k=0; k< T.length(); k++){
        Tmap[T.charAt(k)]++;
    }
    int found=0;
    int length=Integer.MAX_VALUE;
    String res="";
    while(j<S.length()){
        if(found<T.length()){
            if(Tmap[S.charAt(j)]>0){
                Smap[S.charAt(j)]++;
                if(Smap[S.charAt(j)]<=Tmap[S.charAt(j)]){
                    found++;
                }
            }
            j++;
        }
        while(found==T.length()){
            if(j-i<length){
                length=j-i; res=S.substring(i,j);
            }
            if(Tmap[S.charAt(i)]>0){
                Smap[S.charAt(i)]--;
                if(Smap[S.charAt(i)]<Tmap[S.charAt(i)]){
                    found--;
                }
            }
            i++;
        }
    }
    return res;
}

632. Smallest Range 

You have k lists of sorted integers in ascending order. Find the smallest range that includes at least one number from each of the k lists. 

We define the range [a,b] is smaller than range [c,d] if b - a < d - c or a < c if b - a == d - c.

Example 1:

Input:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
Output: [20,24]
Explanation: 
List 1: [4, 10, 15, 24,26], 24 is in range [20,24].
List 2: [0, 9, 12, 20], 20 is in range [20,24].
List 3: [5, 18, 22, 30], 22 is in range [20,24].

Note:

  1. The given list may contain duplicates, so ascending order means >= here.
  2. 1 <= k <= 3500
  3. -10^5 <= value of elements <= 10^5.
  4. For Java users, please note that the input type has been changed to List<List<Integer>>. And after you reset the code template, you'll see this point.

题目解析:

仔细琢磨觉得和上一题很像,只是面对了很多个(k个)列表(相当于上题的t字符串),而s串是 -10^5 到 10^5的整数,或者可以判断一下列表中数的范围。于是我按上一题的思路写了解题方法,可以跑通,但是性能是输的很惨,超时。至少说明代码是对的,双指针+哈希的思想掌握还可以,可以应付面试什么的。

class Solution:
    def smallestRange(self, nums):
        """
        :type nums: List[List[int]]
        :rtype: List[int]
        """
        k = len(nums)
        if k == 1:
            return [nums[0][0], nums[0][0]]

        dict_ = dict(zip(range(k), [0] * k))
        need = dict()
        mins = [arr[0] for arr in nums]
        maxs = [arr[-1] for arr in nums]
        min_of_nums = min(mins)
        max_of_nums = max(maxs)

        start, end = 0, 0
        i = min_of_nums
        missing = k
        for j in range(min_of_nums, max_of_nums + 1):
            for k_ in range(k):
                if j in nums[k_]:
                    need[j] = need.get(j, [])
                    need[j].append(k_)
                    missing -= dict_[k_] == 0
                    dict_[k_] += 1

            if not missing:
                flag = True
                while flag:
                    if i in need.keys():
                        arr = need[i]
                        for n in arr:
                            if dict_[n] == 1:
                                flag = False
                        if flag:
                            for n in arr:
                                dict_[n] -= 1
                            i += 1
                    else:
                        i += 1
                if not end or j - i < end - start:
                        start, end = i, j
                missing += 1
                for n in need[i]:
                    dict_[n] -= 1
                i += 1

        return [start, end]

不过这一题我们还是要学习一下大神的解法(抄袭一下过了这道题吧),下面的解法对上述思路进行了优化,学习一个:

class Solution:
    def smallestRange(self, nums):
        """
        :type nums: List[List[int]]
        :rtype: List[int]
        """
        k = len(nums)
        dic = collections.defaultdict(list)
        total = set()
        for i,num in enumerate(nums):
            for x in num:
                dic[x].append(i)
                total.add(x)
        total = list(total)
        total.sort()
        i = j = 0
        res = float('inf')
        counter = collections.Counter()
        count = 0
        res_i = res_j = 0
        while j<len(total):
            while count<k and j<len(total):
                for t in dic[total[j]]:
                    counter[t] += 1
                    if counter[t] == 1:
                        count += 1
                j += 1
            while count>=k and i<j:
                for t in dic[total[i]]:
                    counter[t] -= 1
                    if counter[t] == 0:
                        count -= 1
                i += 1
            if total[j-1]-total[i-1] < res:
                res = total[j-1]-total[i-1]
                res_i = total[i-1]
                res_j = total[j-1]
        return [res_i,res_j]

然而耗时仍然较高,200ms+, 那些跑得快的解法,应用了最大堆数据结构,我们之后会进行介绍再做补充~


到此字符串的题目就全部结束了,还是十分艰难的吧,主要是结合其他各种算法思想,后面介绍其他算法时仍然会遇到字符串的题目。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值