算法leetcode|76. 最小覆盖子串(rust重拳出击)

这篇具有很好参考价值的文章主要介绍了算法leetcode|76. 最小覆盖子串(rust重拳出击)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。



76. 最小覆盖子串:

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

样例 1:

输入:
	
	s = "ADOBECODEBANC", t = "ABC"
	
输出:
	
	"BANC"
	
解释:
	
	最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

样例 2:

输入:
	
	s = "a", t = "a"
	
输出:
	
	"a"
	
解释:
	
	整个字符串 s 是最小覆盖子串。

样例 3:

输入: 
	
	s = "a", t = "aa"
	
输出: 
	
	""
	
解释:

	 t 中两个字符 'a' 均应包含在 s 的子串中,
	因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105
  • st 由英文字母组成

进阶:

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


分析:

  • 面对这道算法题目,二当家的再次陷入了沉思。
  • 直接采用双层循环暴力解决的话,时间复杂度会比较高,恐怕过不了用例,没有去试。
  • 题目并不要求字符的顺序,只要求了每种字符的数量,那首先就想到要做计数。
  • 接下来考虑如何降低时间复杂度,每次循环都从新匹配子串是低效的,要尽可能复用,滑动窗口在这里非常合适,使用双指针,先直接移动右指针找到第一个满足条件的子串,记下长度作为备选结果,接下来移动左指针,当不满足覆盖子串的条件时就继续移动右置针直到满足覆盖子串的条件,并比较子串的长度,取较小的子串长度作为备选结果,重复该操作,直到循环遍历完毕。
  • 如何高效判断遍历的子串已经满足覆盖子串呢?字符计数这时候就要大展拳脚了,先在遍历字符串 s 之前,先对字符串 t 进行一次遍历,做初始化计数,记录下每种字符的个数(题解中这里使用了减法,使用加法也是可以的,只是后面的加减法就要变),在遍历 s 时,移动右指针就是延长了覆盖子串,同时修改计数,这里的加减法计算要和前面初始化的相反,判断计数是否为0,如果变为0则表示,这种字符的个数已经没有差异了,但是我们需要覆盖了 t 中的每一种字符,所以需要判断 26 个字符的个数是不是都够了,如果都够了,就是满足了覆盖子串,接下来就移动左指针,同时修改计数,这里的加减计算要和右指针移动的相反。
  • 当某个字符的计数变为0时,我们需要判断 26 种字符的字符个数是不是都满足了,这里就需要26次循环,是否有更高效的办法呢?我们可以额外增加一个变量记录有差异的字符种类数(记录有差异的字符数也是可以的,但是后面的逻辑会有一点区别,思想大致相同), 初始化时顺便初始化该变量,在遍历匹配中,每当有字符计数变为0,就修改这个变量,如果这个变量变为0则表示完全覆盖,从而提高效率。
  • 只看文字可能不便理解,建议对照着熟悉的语言的题解一起看,希望可以有助学习理解。

算法leetcode|76. 最小覆盖子串(rust重拳出击),LeetCode力扣算法题目,rust,golang,数据结构,算法,后端,leetcode

题解:

rust:

impl Solution {
    pub fn min_window(s: String, t: String) -> String {
        // 少于目标字符串中数量的字符数量
        let mut diff_char_count = 0;
        // 字符计数器
        let mut cnt = vec![0; 128];

        // 初始化
        t.as_bytes().iter().for_each(|&b| {
            // 计数减少
            cnt[b as usize] -= 1;
            if cnt[b as usize] == -1 {
                // 差异字符数增加
                diff_char_count += 1;
            }
        });

        // 覆盖子串结果信息
        let (mut ans_len, mut ans_l) = (usize::MAX, usize::MAX);

        // 开始滑动窗口
        let s_len = s.len();
        let (mut l, mut r) = (0, 0);
        while r < s_len {
            // 计数增加
            cnt[s.as_bytes()[r] as usize] += 1;

            // 向右移动右边界后,可能该字符数量没有差异了
            if cnt[s.as_bytes()[r] as usize] == 0 {
                // 差异字符数减少
                diff_char_count -= 1;
                // 差异字符数减少后可能为0了
                if diff_char_count == 0 {
                    // 向右滑动左边界,直到会有差异,取满足要求的最小串
                    while cnt[s.as_bytes()[l] as usize] > 0 {
                        cnt[s.as_bytes()[l] as usize] -= 1;
                        l += 1;
                    }

                    // 更新结果
                    if r - l + 1 < ans_len {
                        ans_len = r - l + 1;
                        ans_l = l;
                    }

                    // 向右移动左边界,差异字符数增加
                    cnt[s.as_bytes()[l] as usize] -= 1;
                    l += 1;
                    diff_char_count += 1;
                }
            }
            r += 1;
        }

        return if ans_l == usize::MAX {
            "".to_string()
        } else {
            s[ans_l..ans_l + ans_len].to_string()
        }
    }
}

go:

func minWindow(s string, t string) string {
    // 少于目标字符串中数量的字符数量
	diffCharCount := 0
	// 字符计数器
	cnt := make([]int, 128)

	// 初始化
	for _, c := range t {
		// 计数减少
		cnt[c]--
		if cnt[c] == -1 {
			// 差异字符数增加
			diffCharCount++
		}
	}

	// 覆盖子串结果信息
	ansLen, ansL := math.MaxInt32, -1

	// 开始滑动窗口
	sLen := len(s)
	l, r := 0, 0
	for r < sLen {
		// 计数增加
		cnt[s[r]]++

		// 向右移动右边界后,可能该字符数量没有差异了
		if cnt[s[r]] == 0 {
			// 差异字符数减少
			diffCharCount--
			// 差异字符数减少后可能为0了
			if diffCharCount == 0 {
				// 向右滑动左边界,直到会有差异,取满足要求的最小串
				for cnt[s[l]] > 0 {
					cnt[s[l]]--
					l++
				}

				// 更新结果
				if r-l+1 < ansLen {
					ansLen = r - l + 1
					ansL = l
				}

				// 向右移动左边界,差异字符数增加
				cnt[s[l]]--
				l++
				diffCharCount++
			}
		}
		r++
	}

	if ansL == -1 {
		return ""
	} else {
		return s[ansL : ansL+ansLen]
	}
}

c++:

class Solution {
public:
    string minWindow(string s, string t) {
        // 少于目标字符串中数量的字符数量
        int diffCharCount = 0;
        // 字符计数器
        int cnt[128];
        memset(cnt, 0, sizeof(cnt));

        // 初始化
        const int tLen = t.length();
        for (int i = 0; i < tLen; ++i) {
            if (--cnt[t[i]] == -1) {
                // 差异字符数增加
                ++diffCharCount;
            }
        }

        // 覆盖子串结果信息
        int ansLen = INT_MAX, ansL = -1;

        // 开始滑动窗口
        const int sLen = s.length();
        int l = 0, r = -1;
        while (++r < sLen) {
            // 向右移动右边界后,可能该字符数量没有差异了
            if (++cnt[s[r]] == 0) {
                // 差异字符数减少后可能为0了
                if (--diffCharCount == 0) {
                    // 向右滑动左边界,直到会有差异,取满足要求的最小串
                    while (--cnt[s[l++]] >= 0) {
                        // nothing
                    }

                    // 差异字符数增加
                    ++diffCharCount;

                    // 更新结果
                    if (r - l + 2 < ansLen) {
                        ansLen = r - l + 2;
                        ansL = l - 1;
                    }
                }
            }
        }

        return ansL == -1 ? "" : s.substr(ansL, ansLen);
    }
};

python:

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # 少于目标字符串中数量的字符数量
        diff_char_count = 0
        # 字符计数器
        cnt = collections.defaultdict(int)

        # 初始化
        for c in t:
            # 计数减少
            cnt[c] -= 1
            if cnt[c] == -1:
                # 差异字符数增加
                diff_char_count += 1

        # 覆盖子串结果信息
        ans_len, ans_l = float("inf"), -1

        # 开始滑动窗口
        s_len = len(s)
        l, r = 0, 0
        while r < s_len:
            # 计数增加
            cnt[s[r]] += 1

            # 向右移动右边界后,可能该字符数量没有差异了
            if cnt[s[r]] == 0:
                # 差异字符数减少
                diff_char_count -= 1
                # 差异字符数减少后可能为0了
                if diff_char_count == 0:
                    # 向右滑动左边界,直到会有差异,取满足要求的最小串
                    while cnt[s[l]] > 0:
                        cnt[s[l]] -= 1
                        l += 1

                    # 更新结果
                    if r - l + 1 < ans_len:
                        ans_len = r - l + 1
                        ans_l = l

                    # 向右移动左边界,差异字符数增加
                    cnt[s[l]] -= 1
                    l += 1
                    diff_char_count += 1
            r += 1

        return "" if ans_l == -1 else s[ans_l: ans_l + ans_len]


java:

class Solution {
    public String minWindow(String s, String t) {
        // 少于目标字符串中数量的字符数量
        int diffCharCount = 0;
        // 字符计数器
        final int[] cnt = new int[128];

        // 初始化
        final int tLen = t.length();
        for (int i = 0; i < tLen; ++i) {
            if (--cnt[t.charAt(i)] == -1) {
                // 差异字符数增加
                ++diffCharCount;
            }
        }

        // 覆盖子串结果信息
        int ansLen = Integer.MAX_VALUE, ansL = -1;

        // 开始滑动窗口
        final int sLen = s.length();
        int       l    = 0, r = -1;
        while (++r < sLen) {
            // 向右移动右边界后,可能该字符数量没有差异了
            if (++cnt[s.charAt(r)] == 0) {
                // 差异字符数减少后可能为0了
                if (--diffCharCount == 0) {
                    // 向右滑动左边界,直到会有差异,取满足要求的最小串
                    while (--cnt[s.charAt(l++)] >= 0) {
                        // nothing
                    }

                    // 差异字符数增加
                    ++diffCharCount;

                    // 更新结果
                    if (r - l + 2 < ansLen) {
                        ansLen = r - l + 2;
                        ansL = l - 1;
                    }
                }
            }
        }

        return ansL == -1 ? "" : s.substring(ansL, ansL + ansLen);
    }
}

非常感谢你阅读本文~
欢迎【点赞】【收藏】【评论】三连走一波~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://le-yi.blog.csdn.net/ 博客原创~文章来源地址https://www.toymoban.com/news/detail-694988.html


到了这里,关于算法leetcode|76. 最小覆盖子串(rust重拳出击)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法leetcode|72. 编辑距离(rust重拳出击)

    给你两个单词 word1 和 word2 , 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作: 插入一个字符 删除一个字符 替换一个字符 0 = word1.length, word2.length = 500 word1 和 word2 由小写英文字母组成 面对这道算法题目,二当家的再次陷入了沉思。 编

    2024年02月12日
    浏览(44)
  • 算法leetcode|54. 螺旋矩阵(rust重拳出击)

    给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 m == matrix.length n == matrix[i].length 1 = m, n = 10 -100 = matrix[i][j] = 100 面对这道算法题目,二当家的再次陷入了沉思。 可以每次循环移动一步,判断移到边界就变换方向,巧用数组可以减少逻辑判断

    2024年02月08日
    浏览(48)
  • 算法leetcode|91. 解码方法(rust重拳出击)

    一条包含字母 A-Z 的消息通过以下映射进行了 编码 : 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如, \\\"11106\\\" 可以映射为: \\\"AAJF\\\" ,将消息分组为 (1 1 10 6) \\\"KJF\\\" ,将消息分组为 (11 10 6) 注意,消息不能分组为 (1 11 06) ,因

    2024年02月05日
    浏览(42)
  • 算法leetcode|57. 插入区间(rust重拳出击)

    给你一个 无重叠的 ,按照区间起始端点排序的区间列表。 在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。 0 = intervals.length = 10 4 intervals[i].length == 2 0 = intervals[i][0] = intervals[i][1] = 10 5 intervals 根据 intervals[i][0] 按

    2024年02月09日
    浏览(51)
  • 算法leetcode|85. 最大矩形(rust重拳出击)

    给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。 rows == matrix.length cols == matrix[0].length 1 = row, cols = 200 matrix[i][j] 为 ‘0’ 或 ‘1’ 面对这道算法题目,二当家的再次陷入了沉思。 要不是刚做过 84. 柱状图中最大的矩形 这

    2024年02月08日
    浏览(53)
  • 算法leetcode|48. 旋转图像(rust重拳出击)

    给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。 请不要 使用另一个矩阵来旋转图像。 n == matrix.length == matrix[i].length 1 = n = 20 -1000 = matrix[i][j] = 1000 面对这道算法题目,二当家

    2024年02月01日
    浏览(95)
  • 算法leetcode|79. 单词搜索(rust重拳出击)

    给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

    2024年02月09日
    浏览(59)
  • 算法leetcode|65. 有效数字(rust重拳出击)

    有效数字 (按顺序)可以分成以下几个部分: 一个 小数 或者 整数 (可选)一个 \\\'e\\\' 或 \\\'E\\\' ,后面跟着一个 整数 小数 (按顺序)可以分成以下几个部分: (可选)一个符号字符( \\\'+\\\' 或 \\\'-\\\' ) 下述格式之一: 至少一位数字,后面跟着一个点 \\\'.\\\' 至少一位数字,后面跟着一个

    2024年02月15日
    浏览(45)
  • 算法leetcode|55. 跳跃游戏(rust重拳出击)

    给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 1 = nums.length = 3 * 10 4 0 = nums[i] = 10 5 面对这道算法题目,二当家的再次陷入了沉思。 可能想到要暴力尝试或者是双循环

    2024年02月08日
    浏览(108)
  • 算法leetcode|75. 颜色分类(rust重拳出击)

    给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums , 原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0 、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函数的情况下解决这个问题。 n == nums.length

    2024年02月10日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包