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 所有字符的最小子串。

示例:

  1. 输入: S = "ADOBECODEBANC", T = "ABC"
  2. 输出: "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 实现
  1. function minWindow($s, $t) {
  2. $need = [];
  3. $window = [];
  4. $length = strlen($t);
  5. // 测试用例里有一些奇葩的,这里加个判断
  6. if ($length > strlen($s)) {
  7. return '';
  8. }
  9. // 将寻找的字符串装入 $need 数组
  10. for ($c = 0; $c < $length; $c++) {
  11. $need[$t[$c]] = isset($need[$t[$c]]) ? $need[$t[$c]] + 1 : 1;
  12. }
  13. $left = 0;
  14. $right = 0;
  15. $valid = 0;
  16. // 记录最小覆盖子串的起始索引及长度
  17. $start = 0;
  18. $len = PHP_INT_MAX;
  19. while ($right < strlen($s)) {
  20. // $c 是将移入窗口的字符
  21. $c = $s[$right];
  22. // 右移窗口
  23. $right++;
  24. // 进行窗口内数据的一系列更新
  25. if (isset($need[$c])) {
  26. $window[$c] = isset($window[$c]) ? $window[$c] + 1 : 1;
  27. if ($window[$c] === $need[$c]) {
  28. $valid++;
  29. }
  30. }
  31. // 判断左侧窗口是否要收缩
  32. while ($valid === count($need)) {
  33. // 在这里更新最小覆盖子串
  34. if ($right - $left < $len) {
  35. $start = $left;
  36. $len = $right - $left;
  37. }
  38. // d 是将移出窗口的字符
  39. $d = $s[$left];
  40. // 左移窗口
  41. $left++;
  42. // 进行窗口内数据的一系列更新
  43. if (isset($need[$d])) {
  44. if ($window[$d] === $need[$d]) {
  45. $valid--;
  46. }
  47. $window[$d]--;
  48. }
  49. }
  50. }
  51. // 返回最小覆盖子串
  52. return $len === PHP_INT_MAX ? '' : substr($s, $start, $len);
  53. }
  54. return minWindow($s = "ADOBECODEBANC", $t = "ABC"); // output: BANC

参考链接:

  1. 滑动窗口解题框架思路,强烈推荐这个网站的作者

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券