PHP 滑动窗口算法实现计算最小覆盖子串

PHP 滑动窗口算法实现计算最小覆盖子串

author: he xiaodong date: 2020-06-01
最小覆盖子串

给你一个字符串 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

参考链接:

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