这是之前刷题时遇到到的一眼懵问题,就是看完第一眼整个人都是懵的,不知道在问什么。所以就习惯性的加到了待办事项中,想来已数月有余,这两天在清理待办事项列表,今天就轮到它了。
题目 原地址 
原题是这样的,给一个字符串s,一个字符串t。返回s中涵盖t所有字符的最小子串。如果s中不存在涵盖t所有的字符的子串,则返回空字符串“”。
分析 我自己本身是没有思路的,后来查了网上的解析,简单说就一句话,使用双指针,都从左侧开始,指针区间不满足条件时,右移r,当满足条件时右移l,并记录每次的子串,保存最小子串。
做法不难理解,困难的是要具有这种动态处理问题的思想。
首先,子串必连续,s中每个字符作为子串的开头时,要么不存在目标子串,要么仅存在一个最短的目标子串,原因是,当其符合条件时,再向右扩张,则不是最短的了。当以某个字符开头的所有子串中,不存在符合条件的,那么,其右侧的字符作为开头的子串中,也不会存在符合条件的,原因是,右侧字符开头的字符串是该字符开头的字符串的子集,超集不存在符合条件的字符串,那么子集必然不会存在。
两个指针的移动规则确定了,那么接下来的问题就是,如何对比两个指针之间的子串是否满足目标字符串,最直接的方法就是每次更新完指针的位置后,统计出区间的字符数量,然后再和目标字符串的字符数量,循环一一对比看是否可以覆盖。但是,我们可以容易的发现,在每次移动完指针之后,指针之间字符的变化只有指针所指向的字符,左指针右移动,减少一个对应字符数量,右指针右移,增加一个对应字符的数量,所以,就不必每次都重新计算区间情况,而是做一个缓存,每次只更新必要的字符计数。
代码 代码中写了较为详尽的说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import  kotlin.collections.HashMapimport  kotlin.math.maxfun  minSubStr (s: String , t: String )     if  (s.length < t.length || s.isEmpty()) return  ""      var  subStart = 0      var  subLength = s.length + 1                               val  tDict = HashMap<Char , Int >()     for  (i in  t.indices) {         increment(tDict, t[i])     }     var  left = 0      var  right = 0           val  subDict = HashMap<Char , Int >().apply { put(s[0 ], 1 ) }     while  (left < s.length && right < s.length) {         val  ok = compare(subDict, tDict)         if  (!ok) {             right++             if  (right < s.length) {                 increment(subDict, s[right])             }         } else  {             val  interval = right - left             if  (interval < subLength) {                 subStart = left                 subLength = interval             }             decrement(subDict, s[left])             left++         }     }     return  if  (subLength <= s.length) s.substring(subStart, subStart + subLength + 1 ) else  ""  } 
下面是3个辅助方法,很简单,便不多言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 fun  increment (d: HashMap <Char , Int >, k: Char )     if  (d.containsKey(k)) {         d[k] = d[k]!! + 1      } else  {         d[k] = 1      } } fun  decrement (d: HashMap <Char , Int >, k: Char )     if  (d.containsKey(k)) {         d[k] = max(0 , d[k]!! - 1 )     } } fun  compare (src: HashMap <Char , Int >, target: HashMap <Char , Int >) Boolean  {    for  (key in  target.keys) {         if  (!src.containsKey(key)) return  false          if  (src[key]!! < target[key]!!) return  false      }     return  true  } 
测试用例是从题目里找来的,一共有3个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fun  main ()     println("hello leetcode 76" )     val  s1 = "ADOBECODEBANC"      val  t1 = "ABC"      println("s:$s1 , t:$t1 , sub:${minSubStr(s1, t1)} " )     val  s2 = "a"      val  t2 = "a"      println("s:$s2 , t:$t2 , sub:${minSubStr(s2, t2)} " )     val  s3 = "a"      val  t3 = "aa"      println("s:$s3 , t:$t3 , sub:${minSubStr(s3, t3)} " ) } s:ADOBECODEBANC, t:ABC, sub:BANC s:a, t:a, sub:a s:a, t:aa, sub: