子串相关的问题直观的解法通常时间复杂度都比较高。如下模板可以借鉴解决很多相关问题:
int findSubstring(string s){
vector<int> map(128,0);
int counter; // check whether the substring is valid
int begin=0, end=0; //two pointers, one point to tail and one head
int d; //the length of substring
for() { /* initialize the hash map here */ }
while(end<s.size()){
if(map[s[end++]]-- ?){ /* modify counter here */ }
while(/* counter condition */){
/* update d here if finding minimum*/
//increase begin to make it invalid/valid again
if(map[s[begin++]]++ ?){ /*modify counter here*/ }
}
/* update d here if finding maximum*/
}
return d;
}
具体实例如下:
1. 最小子串窗口问题
给定两个字符串S和T,求在S中包含T中所有字母的最小子串窗口。如S="ADOBECODEBANC",T="ABC",则最小子串窗口为"BANC"。
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
#初始化一个dict,把S中所有字符都记录进去,并且设置其值为0
dict = {c: 0 for c in s}
#对于T中的每一个字符c,如果dict中不包含c,说明T中有S中不含有的字符,直接返回;否则,把dict[c]的值加1(如果c在T中出现k次,那么dict[c]的值就是k,也就是c需要在S中被匹配k次)
for c in t:
if c not in dict:
return ""
dict[c] += 1
#设置计数器counter为T的长度,用于记录需匹配的字符数量
counter = len(t)
#设置循环的起点、终点指针,记录子窗口的长度,以及其起点
begin, end, d, head = 0, 0, 2 ** 30, 0
#对S进行遍历
while end < len(s):
#如果dict[c]值大于0,说明T中含有c,且未被其他前面的字符匹配掉,则匹配成功一个字符,需匹配的字符数counter减1
if dict[s[end]] > 0:
counter -= 1
#把dict[c]的值减1(T中不含有的字符其值都为负数)
dict[s[end]] -= 1
end += 1
#如果counter==0,说明T中所有字符都已经被成功匹配,则开始考虑把窗口的起点进行右移,寻找最优解
while counter == 0:
if end - begin < d:
head = begin
d = end - begin
#如果dict[c]==0,说明dict[c]是T中的字符,跳过该字符后,窗口就不包含T中所有字符了(当counter为0时,dict中的值要么为负数,要么为0,其中值为0,且在s中遍历过的,只有T中包含的字符)。此时将counter加1,说明又有一个字符需匹配了。
if dict[s[begin]] == 0:
counter += 1
#将dict[c]值加1,如果是T中含有的字符,那么此时该值应该为正数,说明T中还有dict[c]个c需匹配。如果不是T中的字符,前面遍历S的时候减一次,这里再加一次,肯定还是非正数
dict[s[begin]] += 1
begin += 1
return s[head: head + d]
2. 最多有两个不同字符的最长子串
给定一个字符串,求其中最多含有两个不同字符的最长子串的长度。如"eceba"的满足要求的子串为"ece","ecebbbbba
def lengthOfLongestSubstringTwoDistinct(self, s):
dict = {c: 0 for c in s}
counter = 0
begin, end, d, head = 0, 0, 0, 0
while end < len(s):
if dict[s[end]] == 0:
counter += 1
while counter > 2:
if end - begin > d:
d = end - begin
head = begin
if dict[s[begin]] == 1:
counter -= 1
dict[s[begin]] -= 1
begin += 1
dict[s[end]] += 1
end += 1
return d
3. 无重复字符的最长子串
给定一个字符串S,求S中不含重复字符的最长子串的长度。如"abcabcbb"的不含重复字符的子串为"abc"
def lengthOfLongestSubstring(self, s):
dict = {c: 0 for c in s}
begin, end, d, head = 0, 0, 0, 0
while end < len(s):
dict[s[end]] += 1
while dict[s[end]] > 1:
d = max(d, end - begin)
dict[s[begin]] -= 1
begin += 1
end += 1
if end == len(s):
d = max(d, end - begin)
return d