声明:
算法基于https://labuladong.github.io/
python语言实现
滑动窗口:子串问题
sliding window template
76. Minimum Window Substring
3. Longest Substring Without Repeating Characters
438. Find All Anagrams in a String
567. Permutation in String
python:
def slidingWindow(self, s: str, t: str) -> str:
window = dict()
need = dict()
for c in t:
need.setdefault(c, 0)
need[c] += 1
left = right = 0
valid = 0 # valid表示窗口中满足need条件的字符个数,如果valid和len(need)的大小相同,则说明窗口已经满足条件,已经完全覆盖了串t。
# answer relevant vars:record answer
for right, in_win_char in enumerate(s):
# 1.增大窗口
# right:右移窗口指针,增大窗口
# in_win_char:移入窗口的字符
window.setdefault(in_win_char, 0)
# 1.2 更新窗口内数据
if in_win_char in need.keys():
window[in_win_char] += 1
if window[in_win_char] == need[in_win_char]:
valid += 1
# 2.debug position:
# print("window: [%d, %d)\n" % (left,right));
# 3.收缩窗口
# 3.1 判断左侧窗口是否要收缩:找到可行解时,左侧窗口即可进行收缩。
while (valid == len(need) | valid >= len(need)):
# 3. 更新答案可能位置1
# 3.3 缩小窗口
# out_win_char:将移出窗口的字符
out_win_char = s[left]
# 缩小窗口
left += 1
# 3.4 更新窗口内数据
if out_win_char in need.keys():
if window[out_win_char] == need[out_win_char]:
valid -= 1
window[out_win_char] -= 1
# 3. 更新答案可能位置2
return
双指针解决如下三类问题:
1.快慢指针:链表、归并排序找中点、链表成环判定
2.左右指针:数组问题、二分搜索、反转数组
3.滑动窗口:子串问题
Note:
python built-in function:
dict()
len(dict)
cmp(dict1,dict2)
3.1dict.get(key[, default=None])
key=>存在:返回key对应的value
不存在:返回default
3.2dict.setdefult(key[, default=None])
类似dict.get(),
但当key不存在时:不会返回default,而是添加该不存在的key,并将该值设为default。
此时若设置了default值,返回default值;若为设置default值,默认为None,无返回值。dict.keys()
# return: list[k1, k2, …]
dict.values()
# return: list[v1, v2, …]
dict.items()
# dict_items([(k1, v1), (k2, v2), …]) 以列表返回可遍历的(k,v)元组数组
dict.has_key(key)
# True/False
dict.pop(key[, default])
# 删除key对应的value,
# key存在:删value,并返回value值;
# key不存在若设置了default:返回default值;
# key不存在default未设置,报错:KeyError。
dict.popitem()
# (last_key, last_value) 删除并返回最后一对k-v
enumerate()
Python中的enumerate()是一个内置函数,用于为可迭代对象的每个项目分配索引。它在可迭代对象上添加一个循环,同时跟踪当前项并以可枚举的形式返回对象。这个对象可以在 for 循环中使用 list() 方法将其转换为列表。
Note:enumerate只能用于for循环,不能用于while循环。
Note:python中的for循环与while循环不等价,不可相互转换。区别于Java,c,c++等语言。
Python 仅使用两个循环:“While 循环”和“for 循环”。
- While 循环 根据条件语句是真还是假 来执行。
- for 循环称为迭代器,它 根据条件集迭代元素。
- Python For 循环还可以用于一组其他各种事情(指定我们要循环的元素集合)
- 断点在 For 循环中用于在任何特定点中断或终止程序
- Continue 语句会继续打印出语句,并根据条件集打印出结果
- “for循环”中的枚举函数返回我们正在查看的带有索引号的集合成员
enumerate(sequence[, start=0])
sequence: 可遍历对象:list、string、tuple、dict…
组合为(start,value)索引序列 # start:对应index_0
***一般用于for循环,当索引start位不在for循环中使用时,可使用_单下划线作为占位符- example:
>>>list1=['a','b','c']
>>>enumerate(list1)
>>>list2=enumerate(list1)
>>>list2
>>>list2=list(enumerate(list1))
>>>list2
[(0, ‘a’), (1, ‘b’), (2, ‘c’)]
>>>for i,element in enumerate(list1):print(i,element)
0 a
1 b
2 c
>>>for _,element in enumerate(list1):print(element)
a
b
c
>>>
java:
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 增大窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 缩小窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
https://leetcode.com/problems/minimum-window-substring/
python:
solution1:使用window+need
class Solution:
def minWindow(self, s: str, t: str) -> str:
if len(s) < len(t):
return ""
# Note:不能初始化为window=need=dict(),否则二者指向同一对象,即:不同变量分开初始化!
window = dict()
need = dict()
for c in t:
need.setdefault(c, 0)
need[c] += 1
# print(need)
left = 0
right = 0
valid = 0
min_len = len(s) + 1
min_str = ""
# expand window:
# Note:enumerate只能用于for循环,不能用于while循环
for right, in_win_char in enumerate(s):
# right:右窗口指针,右移扩大窗口
# win_in_char:移入窗口中的字符
# update window
if in_win_char in need.keys():
window.setdefault(in_win_char, 0)
window[in_win_char] += 1
if window[in_win_char] == need[in_win_char]:
valid += 1
# print("window[%c]:%d\n" % (in_win_char,window[in_win_char]))
# print("need[%c]:%d\n" % (in_win_char,need[in_win_char]))
# print("valid:%d\n" % valid)
# debug position
# print("window:[%d, %d)\n" % (left, right))
# shrink window:找到可行解时,通过收缩窗口这一动作优化可行解,使其向最优解收缩
while (valid == len(need)):
# update answer
if right - left < min_len:
min_len = right - left
min_str = s[left:right + 1]
# out_win_char:移出窗口中的字符
out_win_char = s[left]
# left:左窗口指针,右移缩小窗口
left += 1
if out_win_char in need.keys():
if window[out_win_char] == need[out_win_char]:
valid -= 1
window[out_win_char] -= 1
return min_str
def main():
# solution1=Solution()
# print(solution1.minWindow("ADOBECODEBANC", "ABC"))
#
# solution2=Solution()
# print(solution2.minWindow("a", "a"))
#
# solution2=Solution()
# print(solution2.minWindow("a", "aa"))
# wrong answer
# i:aa
# o:a
# e:aa
# wrong reason:初始化为window=need=dict(),二者指向同一对象。
solution2=Solution()
print(solution2.minWindow("aa", "aa"))
if __name__ == '__main__':
main()
solution2:仅使用need
class Solution:
def minWindow(self, s: str, t: str) -> str:
need = dict()
for c in t:
need.setdefault(c, 0)
need[c] += 1
left = 0
right = 0
valid = 0 # valid表示窗口中满足need条件的字符个数,如果valid和len(need)的大小相同,则说明窗口已经满足条件,已经完全覆盖了串t。
#记录最小覆盖子串
min_size = len(s)+1 # 最小覆盖子串长度初始化为字符串s长度加1
min_str = ""
for right, in_win_char in enumerate(s):
# right:右移窗口指针,in_win_char:移入窗口的字符
#窗口内数据更新
if in_win_char in need:
need[in_win_char] -= 1
if need[in_win_char] == 0:
valid += 1
# 找到可行解
while (valid == len(need)):
# 记录可行解
win_size = right - left
#更新最小覆盖子串
if win_size < min_size:
min_str = s[left:right+1]
min_size = win_size
# 移动左指针优化可行解,out_win_char是将移出窗口的字符
out_win_char = s[left]
if out_win_char in need:
need[out_win_char] += 1
if need[out_win_char] == 1:
valid -= 1
left += 1
return min_str
java:
string minWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
// 记录最小覆盖子串的起始索引及长度
int start = 0, len = INT_MAX;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
if (need.count(c)) {
window[c]++;
if (window[c] == need[c])
valid++;
}
// 判断左侧窗口是否要收缩
while (valid == need.size()) {
// 在这里更新最小覆盖子串
if (right - left < len) {
start = left;
len = right - left;
}
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
if (need.count(d)) {
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
// 返回最小覆盖子串
return len == INT_MAX ?
"" : s.substr(start, len);
}
https://leetcode.com/problems/longest-substring-without-repeating-characters/
python:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
window = dict()
left = 0
right = 0
res = 0
# expand window
for right, in_win_char in enumerate(s):
# update window
window.setdefault(in_win_char, 0)
window[in_win_char] += 1
# shrink window
while window[in_win_char] > 1:
# shrink window
out_win_char = s[left]
left += 1
# update window
window[out_win_char] -= 1
# update answer
res = max(res, right - left + 1)
return res
def main():
# Input: s = "abcabcbb"
# Output: 3
# Explanation: The answer is "abc", with the length of 3.
solution1 = Solution()
print(solution1.lengthOfLongestSubstring("abcabcbb"))
# Input: s = "bbbbb"
# Output: 1
# Explanation: The answer is "b", with the length of 1.
solution2 = Solution()
print(solution2.lengthOfLongestSubstring("bbbbb"))
if __name__ == '__main__':
main()
https://leetcode.com/problems/find-all-anagrams-in-a-string/
python:
class Solution:
def findAnagrams(self, s: str, p: str):
window = dict()
need = dict()
for c in p:
need.setdefault(c, 0)
need[c] += 1
left = 0
right = 0
valid = 0
res = []
# expand window
for right, in_win_char in enumerate(s):
# update window
if in_win_char in need.keys():
window.setdefault(in_win_char, 0)
window[in_win_char] += 1
if window[in_win_char] == need[in_win_char]:
valid += 1
# shrink window
while right - left + 1 >= len(p):
# update answer
if valid == len(need):
res.append(left)
out_win_char = s[left]
left += 1
if out_win_char in need.keys():
if window[out_win_char] == need[out_win_char]:
valid -= 1
window[out_win_char] -= 1
return res
def main():
# Output: [0,1,2]
solution1 = Solution()
print(solution1.findAnagrams("abab", "ab"))
# Output: [0,6]
solution2 = Solution()
print(solution2.findAnagrams("cbaebabacd", "abc"))
# "baa"
# "aa"
# Output []
# Expected [1]
solution3 = Solution()
print(solution3.findAnagrams("baa", "aa"))
if __name__ == '__main__':
main()
https://leetcode.com/problems/permutation-in-string/
python:
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
window = dict()
need = dict()
for c in s1:
need.setdefault(c, 0)
need[c] += 1
left = 0
right = 0
valid = 0
# expand window
for right, in_win_char in enumerate(s2):
# update window
if in_win_char in need.keys():
window.setdefault(in_win_char, 0)
window[in_win_char] += 1
if window[in_win_char] == need[in_win_char]:
valid += 1
# shrink window
while right - left + 1 >= len(s1):
# update answer
if valid == len(need):
return True
# shrink window
out_win_char = s2[left]
left += 1
if out_win_char in need.keys():
if window[out_win_char] == need[out_win_char]:
valid -= 1
window[out_win_char] -= 1
return False
def main():
# Input: s1 = "ab", s2 = "eidbaooo"
# Output: true
# Explanation: s2 contains one permutation of s1("ba").
solution1 = Solution()
print(solution1.checkInclusion("ab", "eidbaooo"))
# Input: s1 = "ab", s2 = "eidboaoo"
# Output: false
solution2 = Solution()
print(solution2.checkInclusion(s1 = "ab", s2 = "eidboaoo"))
if __name__ == '__main__':
main()