layout: posttitle: PHP 滑动窗口算法实现计算最小覆盖子串
subtitle: PHP 滑动窗口算法实现计算最小覆盖子串
date: 2020-06-01
author: he xiaodong
header-img: img/default-post-bg.jpg
catalog: true
tags:
- Go
- PHP
- LeetCode
- 最小覆盖子串
- 滑动窗口算法
最小覆盖子串
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串 “”。
- 如果 S 中存在这样的子串,我们保证它是唯一的答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
解题思路
1、我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。
2、我们先不断地增加 right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求(包含了 T 中的所有字符)。
3、此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。
这个思路其实也不难,第 2 步相当于在寻找一个 「可行解」,然后第 3 步在优化这个可行解」,最终找到最优解,也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动,这就是 「滑动窗口」这个名字的来历。
PHP 实现
function minWindow($s, $t) {$need = [];$window = [];$length = strlen($t);// 测试用例里有一些奇葩的,这里加个判断if ($length > strlen($s)) {return '';}// 将寻找的字符串装入 $need 数组for ($c = 0; $c < $length; $c++) {$need[$t[$c]] = isset($need[$t[$c]]) ? $need[$t[$c]] + 1 : 1;}$left = 0;$right = 0;$valid = 0;// 记录最小覆盖子串的起始索引及长度$start = 0;$len = PHP_INT_MAX;while ($right < strlen($s)) {// $c 是将移入窗口的字符$c = $s[$right];// 右移窗口$right++;// 进行窗口内数据的一系列更新if (isset($need[$c])) {$window[$c] = isset($window[$c]) ? $window[$c] + 1 : 1;if ($window[$c] === $need[$c]) {$valid++;}}// 判断左侧窗口是否要收缩while ($valid === count($need)) {// 在这里更新最小覆盖子串if ($right - $left < $len) {$start = $left;$len = $right - $left;}// d 是将移出窗口的字符$d = $s[$left];// 左移窗口$left++;// 进行窗口内数据的一系列更新if (isset($need[$d])) {if ($window[$d] === $need[$d]) {$valid--;}$window[$d]--;}}}// 返回最小覆盖子串return $len === PHP_INT_MAX ? '' : substr($s, $start, $len);}return minWindow($s = "ADOBECODEBANC", $t = "ABC"); // output: BANC
参考链接:
- 滑动窗口解题框架思路,强烈推荐这个网站的作者
