题目大意:在一个字符串S中找一个最短子串,使得这个最短子串中包含另外一个字符串T中的所有字符。
解法:
首先向这种乍一看似乎是n^2复杂度,且又必须通过线性时间复杂度来完成的问题一般都可以利用动态规划来解决。但是这里并不是直接使用动态规划,而是使用动态规划的思路,复用前面的结果来计算剩余部分。
假设S[i...j]是所有以i起始包含T中所有字符的子串中的最短者,我们称j是i的右界。若j是i的右界,那么i+1的右界必然不可能小于j。因为若i+1的右界k<j,则S[i+1...k]也是包含字符串T的一个S的子串,那么S[i...k]中包含了S[i+1...k]中的所有字符,因此S[i...k]也是包含字符串T的一个S的子串,这与j是i的右界的定义相悖,故假设不成立,从而命题得证。
因此我们只需要维护两个下标i和j,j作为i可能的右界,显然在i递增的前提下可以同样推出j不减,因此只要保证每次循环或许增大i或者增大j,就可以以线性时间得到结果。
具体的流程是创建一个需求表D,需求表中保存的键值对k:v,表示S[i...j]要包含T还缺少v个字符k。因此每次增加i后,先令D[S[i-1]]自减,之后不断增大j,每次增大j后都需要令D[S[j-1]]自增,直到D中所有的项都不超过0,这样就能保证j是i的右界。至于如果能快速判断D中所有项不超过0,可以通过一个计数器counter,counter记录当前D中大于0的项的个数,之后不断检测counter即可。
实现:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class Solution { 2 int[] need; 3 int counter; 4 public String minWindow(String s, String t) { 5 need = new int[256]; 6 counter = 0; 7 for(int i = 0, bound = t.length(); i < bound; i++) 8 { 9 char c = t.charAt(i); 10 if(need[c] == 0) 11 { 12 counter++; 13 } 14 need[c]++; 15 } 16 17 18 int minL = -1; 19 int min = Integer.MAX_VALUE; 20 for(int l = 0, r = 0, bound = s.length(); l < bound; l++) 21 { 22 while(counter > 0 && r < bound) 23 { 24 add(s.charAt(r)); 25 r++; 26 } 27 28 if(counter > 0) 29 { 30 break; 31 } 32 if(r - l < min) 33 { 34 min = r - l; 35 minL = l; 36 } 37 delete(s.charAt(l)); 38 } 39 40 if(minL < 0) 41 { 42 return ""; 43 } 44 return s.substring(minL, minL + min); 45 } 46 47 private void delete(char c) 48 { 49 if(need[c] == 0) 50 { 51 counter++; 52 } 53 need[c]++; 54 } 55 private void add(char c) 56 { 57 if(need[c] == 1) 58 { 59 counter--; 60 } 61 need[c]--; 62 } 63 }