这一节两道题颇有难度,hard层次的, 可谓需要对双指针和哈希二者理解十分透彻,当然了,一些特定的处理技巧也是很重要的。这两道题比较思想比较接近,建议认真研读,弄清这一类问题。
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;
}
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:
- The given list may contain duplicates, so ascending order means >= here.
- 1 <= k <= 3500
- -10^5 <=
value of elements
<= 10^5. - 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+, 那些跑得快的解法,应用了最大堆数据结构,我们之后会进行介绍再做补充~
到此字符串的题目就全部结束了,还是十分艰难的吧,主要是结合其他各种算法思想,后面介绍其他算法时仍然会遇到字符串的题目。