困难级别的问题,不出意外把我困难住了,纠结了半天能否用动态规划进行解决,没想到动态规划的具体解决办法,所以还是直接去看题解了。题解使用了滑动窗口,印象中这是第一次看到滑动窗口算法,之前只在计算机网络中接触过。具体的算法内容如下:
假设窗口的左右边界分别为 leftW 和 rightW
1)首先固定 leftW ,不断前移增加 rightW,直到窗口内的字符串满足要求。
2)之后固定 rightW,不断前移left,去除无用字符,直指满足要求的最小窗口,记录此时窗口长度与位置。
3)前移 leftW 后,再次将其固定,重复1)和 2),直指窗口滑动至字符串末尾。
所面临的问题
1)如何判断窗口包含所有字符
解:运用字典类型,针对子串中的每一个字符存储所需要的个数,每次滑动到一个字符,对应字符--,<=0 时表明该字符已被窗口包含。
优化:每次都需要对创建的字典类型进行遍历,以判断字符串是否满足要求,浪费时间。
思路:用一个整数 flag 代表字典的长度,每当一个字典元素<=0,整数-1;>0 时整数加1,整数为0时表明窗口包含全部字符。
2)想要优化的话,如何忽略多余判断的字符(题解中的优化问题),比如 [XXXXABC],子串是ABC,如何省略判断前面的多个 XXXX
判断 leftW 对应的字符是否是子串的字符,移动 leftW 直至是子串字符,再对其固定移动rightW。
实现代码如下:
func minWindow(s string, t string) string {
left, right := 0, 0
min := math.MaxInt32
l := len(s)
substrMap := make(map[byte]int)
for i:=0; i0; rightW++{
vr:=s[rightW]
if _, ok := substrMap[vr]; ok{
substrMap[vr]--
if substrMap[vr]==0{
flag--
}
}else{
continue
}
}
}else{
continue
}
if flag==0{ // 构成一个窗口,记录长短
// fmt.Println(leftW, rightW)
if min>rightW-leftW+1{
left,right=leftW,rightW
min=rightW-leftW+1
}
substrMap[vl]++
if substrMap[vl]>0{
flag++
}
}
}
return string(s[left:right])
}
提交结果如下
与题解的运行结果相比,时间与内存均降低了(第一个是题解的提交结果,第二个是自己代码的提交结果)