LeetCode76--最小覆盖子串

谁践踏了优雅 2023-01-13 05:48 235阅读 0赞

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:”BANC”
示例 2:
输入:s = “a”, t = “a”
输出:”a”

提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母组成

进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

解法一 滑动窗口

用双指针 left 和 right 表示一个窗口。

  1. right 向右移增大窗口,直到窗口包含了所有要求的字母。进行第二步。
  2. 记录此时的长度,left 向右移动,开始减少长度,每减少一次,就更新最小长度。直到当前窗口不包含所有字母,回到第 1 步。

    <?php

    class Solution {

    1. /**
    2. * @param String $s
    3. * @param String $t
    4. * @return String
    5. */
    6. function minWindow($s, $t) {
    7. if(strlen($s)<strlen($t)){
    8. return "";
    9. }
  1. $map = [];
  2. //遍历字符串$t,初始化每个字母的次数
  3. for($i=0; $i<strlen($t); $i++){
  4. $map[$t[$i]]++;
  5. }
  6. $left = 0;//左指针
  7. $right = 0;//右指针
  8. $ans_left = 0;//保存最小窗口的左边界
  9. $ans_right = -1;//保存最小窗口的右边界
  10. $ans_len = PHP_INT_MAX;//当前最小窗口的长度
  11. $flag = false;//是否能匹配到的标志
  12. //遍历字符串$s
  13. while ($right<strlen($s)){
  14. if(isset($map[$s[$right]])){
  15. $map[$s[$right]]--;
  16. while ($this->match($map)){
  17. $flag = true;
  18. //当前窗口大小
  19. $tmp_len = $right-$left+1;
  20. if($tmp_len<$ans_len){
  21. $ans_left = $left;
  22. $ans_right = $right;
  23. $ans_len = $tmp_len;
  24. }
  25. //判断$map中是否有当前字母
  26. if(isset($map[$s[$left]])){
  27. //因为要把当前字母移除,所以相应次数要加1
  28. $map[$s[$left]]++;
  29. }
  30. //左指针右移
  31. $left++;
  32. }
  33. }
  34. //右指针右移扩大窗口
  35. $right++;
  36. }
  37. if($flag){//匹配到了
  38. return substr($s, $ans_left, $ans_len);
  39. }else{//未匹配到
  40. return "";
  41. }
  42. }
  43. //判断所有的$v是否为0
  44. function match($map){
  45. foreach ($map as $v){
  46. if($v>0){
  47. return false;
  48. }
  49. }
  50. return true;
  51. }
  52. }

时间复杂度:O(nm),n 是 S 的长度,match 函数消耗 O(m)。
空间复杂度:O(m),m 是 T 的长度。

此外,判断当前窗口是否含有所有字母,我们除了可以判断所有字母的次数是否小于等于 0,还可以用一个计数变量 count,把 count 初始化为 t 的长度,然后每次找到一个满足条件的字母,count 就减 1,如果 count 等于了 0,就代表包含了所有字母。这样的话,可以把之前的 match(map) 优化到 O(1)。

  1. class Solution {
  2. /**
  3. * @param String $s
  4. * @param String $t
  5. * @return String
  6. */
  7. function minWindow($s, $t) {
  8. $s_len = strlen($s);
  9. $t_len = strlen($t);
  10. if($s_len<$t_len){
  11. return "";
  12. }
  13. $map = [];
  14. //遍历字符串$t,初始化每个字母的次数
  15. for($i=0; $i<$t_len; $i++){
  16. $map[$t[$i]]++;
  17. }
  18. $left = 0;//左指针
  19. $right = 0;//右指针
  20. $ans_left = 0;//保存最小窗口的左边界
  21. $ans_right = -1;//保存最小窗口的右边界
  22. $ans_len = PHP_INT_MAX;//当前最小窗口的长度
  23. $flag = false;//是否能匹配到的标志
  24. //遍历字符串$s
  25. while ($right<strlen($s)){
  26. if(isset($map[$s[$right]])){
  27. //当前字母次数减一
  28. $map[$s[$right]]--;
  29. if($map[$s[$right]]>=0){
  30. //代表当前符合了一个字母
  31. $t_len--;
  32. }
  33. //echo $left.'--'.$right.'--'.$t_len.PHP_EOL;
  34. while ($t_len == 0){
  35. $flag = true;
  36. //当前窗口大小
  37. $tmp_len = $right-$left+1;
  38. if($tmp_len<$ans_len){
  39. $ans_left = $left;
  40. $ans_right = $right;
  41. $ans_len = $tmp_len;
  42. }
  43. //判断$map中是否有当前字母
  44. if(isset($map[$s[$left]])){
  45. //因为要把当前字母移除,所以相应次数要加1
  46. $map[$s[$left]]++;
  47. if($map[$s[$left]]>0){
  48. $t_len++;
  49. }
  50. }
  51. //左指针右移
  52. $left++;
  53. }
  54. }
  55. //右指针右移扩大窗口
  56. $right++;
  57. }
  58. if($flag){//匹配到了
  59. return substr($s, $ans_left, $ans_len);
  60. }else{//未匹配到
  61. return "";
  62. }
  63. }
  64. }

发表评论

表情:
评论列表 (有 0 条评论,235人围观)

还没有评论,来说两句吧...

相关阅读