本文共 3014 字,大约阅读时间需要 10 分钟。
https://leetcode-cn.com/problems/minimum-window-substring/
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC” 说明:如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。这个题目关键是怎么判断某个连续字符串不按顺序的包含给定的字符串,依次比较的话是很麻烦的。
但我们可以换一种思路,统计当前窗口内的有效字符数量。这里的有效字符是指某个字符属于给定的目标字符串之一,且该字符当前累积数量小于给定字符数量时,可统计为有效字符count
。
按照这个思路,固定左边界left
,一直往右遍历字符,直到统计到的有效字符数达到目标字符数,则肯定包含了目标字符串。
此时,需要左边界left++
来右移,去掉无效的字符。这里无效的字符包括两类:
左边界右移的结束条件为某个有效字符的累积数量首次小于目标字符数量。
左边界右移结束后,将右边界right
确定为当前趟遍历的下标i
,此时左右边界框定窗口就是包含了目标字符串的覆盖子串!随后计算窗口长度是否小于此前的最小窗口长度min,如果小于就替换min,并将当前窗口内的字符串替换minResult
。
最后将有效字符数count
减一表示已经不满足,并将窗口左边界left右移1位,并开始下一趟遍历。
class Solution { public String minWindow(String s, String t) { if(s == null || t == null || s.length() < t.length()){ return ""; } int[] targetCharCnt = new int[128]; int[] charCnt = new int[128]; char[] cs = t.toCharArray(); for(char c : cs){ targetCharCnt[c] += 1; } // 当前有效字符数 int count = 0; cs = s.toCharArray(); int left = 0; int right = 0; int min = 0; String minResult = ""; for(int i = 0; i < cs.length; i++){ char c = cs[i]; if(targetCharCnt[c] > 0){ if(targetCharCnt[c] > charCnt[c]){ // 此时该字符数量还未达到目标数量,需要累计 count++; } // 累加窗口内的该字符数量 charCnt[c]++; } if(count == t.length()){ // 说明找到了包含目标字符串的字符串 // 此时我们需要缩小左边界,去掉无效字符 while(count == t.length()){ char leftC = cs[left]; if(charCnt[leftC] > 0){ if(--charCnt[leftC] < targetCharCnt[leftC]){ //说明是靠右的有效字符 break; }else{ // 说明是靠左的超过目标重复次数的字符,忽略该字符,继续右移左边界 // 比如需要两个B,即BB,但在这个字符串中有三个B,如ABDDBCB,则忽略最左那个B left++; } }else{ // 非目标字符,右移左边界 left++; } } // 更新右边界为当前字符下标 right = i; if(min == 0){ // 初始时 min = right - left + 1; minResult = s.substring(left, right + 1); }else if(min > right - left + 1){ // 之前有符合条件字符串,且当前找到的字符串长度小于之前的,就替换 min = right - left + 1; minResult = s.substring(left, right + 1); } // 有效字符减一,窗口左边界右移一位 count--; left++; } } return minResult; }}
O©
转载地址:http://ympkb.baihongyu.com/