Java【手撕滑动窗口】LeetCode 438. “字符串中所有异位词“, 图文详解思路分析 + 代码

这篇具有很好参考价值的文章主要介绍了Java【手撕滑动窗口】LeetCode 438. “字符串中所有异位词“, 图文详解思路分析 + 代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你:
📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
📗 Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
📘 JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

一、字符串中所有异位词

1, 题目

OJ链接

以示例1为例, 给定字符串 s 为 : "cbaebabacd", 字符串 p 为"abc", "abc""cba", "acb", "bca" 等是异位词, 要求在 s 中找到 p 的所有异位词, 意思就是在 s 中找到连续的子区间, 这段区间是 p 的异位词

  • 关于异位词
    我们只需要 s 的连续子区间内找到包含 ‘a’, ‘b’, ‘c’ 的三个字符即可, 不一定非要是"abc", 这点很重要 ! !

一般来说, 如果我们研究的对象是 “连续的区间” 就可以考虑滑动窗口

滑动窗口其实就是"同向双指针", 滑动窗口的特点是, 前后两个指针不会回退, 并且窗口总是向前滑动, 窗口不是固定大小的, 可能边长也可能变短, 如果你在分析题目的时候发现了这些特征, 那就基本是滑动窗口的解法了


2, 思路分析

首先可以确定的是, 一定要有两个哈希表, 一个哈希表来记录字符串 p 中的字符出现了几次, 一个哈希表用来记录在字符串 s 的子区间中的字符出现了几次

基本思路 :

  • 1, 为了方便, 可以将字符串 s 和 p 分别转化为字符数组 : arrayS 和 arrayP
  • 2, 定义两个哈希表, hashP 和 hashWindow
  • 3, 定义 left 和 right 指针, 初始位置都从0开始, left 用于标记子序列的左边界, right 用于标记子序列的右边界

题目中说明了字符都是 ‘a’ ~ ‘z’ 的小写字符, 为了进一步提高效率, 可以直接使用长度为 26 的 int[] 数组来模拟哈希表, 用字符来映射出 0~25下标, 例如字符 ‘a’ 在哈希表中的下标就是 ‘a’ - ‘a’ = 0, 字符 ‘c’ 在哈希表中的下标就是 ‘c’ - ‘a’ = 2


2.1, 引入哈希表找出异位词

首先要把 arrayP (字符串 p 的字符数组) 中的字符在哈希表中映射, 出现几次就把哈希表中对应下标的值设为几

然后在 arrayS (字符串 s 的字符数组) 中找到的字符也在哈希表中映射, 同上

要注意, 使用者两个哈希表的目的是记录 “字符出现的次数”, 而不是"字符出现的个数"
例如要找 “bbc” 的异位词, ‘b’ 在哈希表中出现两次, ‘c’ 在哈希表中出现一次, 重点是字符出现的次数而不是个数

由于我们要找的子区间长度是和字符串 p 长度一致的, 所以要让 right 和 left 维护子区间的长度

Java【手撕滑动窗口】LeetCode 438. “字符串中所有异位词“, 图文详解思路分析 + 代码,OJ题,java,leetcode,滑动窗口,双指针,异位词

此时 right 和 left 维护的子区间的长度和 arrayP 长度一致, 并且我们发现两个哈希表中的所记录的 “字符出现的次数” 是一致的, 那就可以认为我们找到了一个异位词

这里可以做优化 ! ! 如何判断两个哈希表中的所记录的 “字符出现的次数” 是一致的?

如果每次在 arrayS 中找到长度为 3 的子区间都遍历两个 hash 表, 是比较浪费时间的, 我们可以引入一个变量 count 用于记录"有效字符的个数"


2.2, 引入变量记录"有效字符的个数"

首先图解一下什么情况下, 字符为有效, 什么情况下不有效

注意观察不同示例下的 arrayS 和 arrayP
Java【手撕滑动窗口】LeetCode 438. “字符串中所有异位词“, 图文详解思路分析 + 代码,OJ题,java,leetcode,滑动窗口,双指针,异位词
综上所述, right 每遍历到一个新的字符时, 就可以对比两个哈希表中的值来判断该字符是否有效

  • hashWindow[arrayS[right] - 'a'] <= hashP[arrayS[right] - 'a'] 时, 为有效字符, 此时可以让变量 count++
  • count == arrayP 时, 说明 right 和 left 维护的子区间的字符全都是有效字符, 即全都是 arrayP 中的字符, 此时可以把 left 加入到 list 中, 作为返回结果

2.3, left 右移维护窗口

上面说过, 假设我们要找的异位词的长度为 3 , 那么 right 和 left 维护的子区间(窗口)的长度就不能大于 3, 当窗口长度为 3 时, 判断 count 的值以判断是否为异位词

所以当窗口长度大于 3 时, 需要让 left 右移来实现"滑动窗口", 这一步有值得注意的细节Java【手撕滑动窗口】LeetCode 438. “字符串中所有异位词“, 图文详解思路分析 + 代码,OJ题,java,leetcode,滑动窗口,双指针,异位词

本题的窗口在"滑动:过程中长度总是为 arrayP.length, 在"滑动窗口"中属于窗口长度不变的类型文章来源地址https://www.toymoban.com/news/detail-693980.html


2.4, 总结核心步骤

  • 1, right 指向新字符时, 在 hashWindow 中更新该字符出现的次数
  • 2, 判断 right 指向的字符是否为有效字符, 如果是, 令 count++
  • 3, 判断窗口大小是否大于 arrayP.length, 如果是, 令 left–
  • 4, 在 left-- 之前判断当前 left 指向的字符是否为有效字符, 如果是, 则 count- -
  • 5, 判断 count 是否和 arrayP.length 相等, 如果是, 令当前 left 存入 list 中

3, 代码

	public List<Integer> findAnagrams(String s, String p) {
        List<Integer> ret = new ArrayList<>();
        char[] arrayP = p.toCharArray();
        char[] arrayS = s.toCharArray();
        int[] hashP = new int[26];
        int[] hashWindow = new int[26];
        for(char ch : arrayP) {
            hashP[ch-'a']++;
        }
        int left = 0;
        int right = 0;
        int count = 0;// 有效字符个数
        while(right < arrayS.length) {
            hashWindow[arrayS[right] - 'a']++;
            if(hashWindow[arrayS[right] - 'a'] <= hashP[arrayS[right] - 'a']) {
                count++;
            }
            if(right - left + 1 > arrayP.length) {
                // left 右移之前要先判断是否为有效字符
                if(hashWindow[arrayS[left] - 'a'] <= hashP[arrayS[left] - 'a']) {
                    // 说明是有效字符
                    count--;
                }
                // hash表里面的该字符也要自减一次
                hashWindow[arrayS[left] - 'a']--;
                left++;
            }
            if(count == arrayP.length) {
                ret.add(left);
            }
            right++;
        }
        return ret;
    }

到了这里,关于Java【手撕滑动窗口】LeetCode 438. “字符串中所有异位词“, 图文详解思路分析 + 代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 面试手撕leetcode 557. 反转字符串中的单词 III

            我的思路是用split方法将字符串分割成若干个单词,然后分别将这些单词反转操作,并逐一加入StringBuilder字符串中。每加入一个单词,需要空一格,但是最后一个单词不需要空格。 代码如下:

    2024年04月26日
    浏览(44)
  • 力扣精选算法100题——找到字符串中所有字母异位词(滑动窗口专题)

    本题链接👉找到字符串中所有字母异位词 给定2个字符串s和p,找到s中所有p的变位词的字串,就是p是\\\"abc\\\",在s串中找到与p串相等的字串,可以位置不同,但是字母必须相同,比如”bca\\\",\\\"bac\\\"等,都是可以被称之为变位词。最终返回与p串字母相等但排列不同的字符串的初始索引

    2024年01月19日
    浏览(67)
  • rust踩雷笔记(2)——一道hard带来的思考[哈希表、字符串、滑动窗口]

    今天被一道hard恶心坏了,算法不难,用C++几分钟的事。用rust主要还是缺乏对语言的熟练度,记录一下,主要的坑在下面这个操作: 对String取其中某个位置的char。 可能你会有疑问:这不是直接nth()取就行了么。没错,我看有些题解也是这样做的,但是那样会在某些字符长度很

    2024年02月12日
    浏览(36)
  • 力扣hot100 找到字符串中所有字母异位词 滑动窗口 双指针 一题双解

    Problem: 438. 找到字符串中所有字母异位词 👩‍🏫 参考题解 ⏰ 时间复杂度: O ( n ) O(n) O ( n ) 🌎 空间复杂度: O ( 1 ) O(1) O ( 1 ) ⏰ 时间复杂度: O ( n ) O(n) O ( n ) 🌎 空间复杂度: O ( 1 ) O(1) O ( 1 )

    2024年01月24日
    浏览(49)
  • Java【手撕滑动窗口】LeetCode 209. “长度最小子数组“, 图文详解思路分析 + 代码

    各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: 📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等 📗 Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等 📘 JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议

    2024年02月11日
    浏览(44)
  • 438. 找到字符串中所有字母异位词【异位词-哈希数组】

    438. 找到字符串中所有字母异位词 给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。 示例 1: 输入: s = “cbaebabacd”, p = “abc” 输出: [0,6] 解释: 起

    2023年04月27日
    浏览(40)
  • 代码随想录复习 1047. 删除字符串中的所有相邻重复项 150 逆波兰表达式求值 239 滑动窗口最大值

    1047. 删除字符串中的所有相邻重复项 代码如下  func removeDuplicates(s string) string {             var  stack []byte   //结果栈数组             for i := 0 ; i  len(s) ; i++ {                 if len(stack)  0  stack[len(stack)-1] == s[i] {  //如果当前遍历到的元素

    2024年02月05日
    浏览(71)
  • 【算法挨揍日记】day07——904. 水果成篮、438. 找到字符串中所有字母异位词

    904. 水果成篮 你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组  fruits  表示,其中  fruits[i]  是第  i  棵树上的水果  种类  。 你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果: 你只有  两个

    2024年02月07日
    浏览(41)
  • 【字符串 简单】LeetCode 14. 最长公共前缀 Java

    我的思路: 给字符串数组按照字符串的长度从长到短排序,因为最长公共前缀最长的话,也只能是字符串数组中最短的那一个字符串 设置一个index变量,表示当前正在检查字符数组中所有字符串的index位置 循环遍历字符串数组n遍,n也就是最长公共前缀的长度 其他思路,方法

    2024年02月15日
    浏览(40)
  • 【Py/Java/C++三种语言详解】LeetCode每日一题240109【动态规划】LeetCode2707题、字符串中的额外字符

    给你一个下标从 0 开始的字符串 s 和一个单词字典 dictionary 。你需要将 s 分割成若干个 互不重叠 的子字符串,每个子字符串都在 dictionary 中出现过。 s 中可能会有一些 额外的字符 不在任何子字符串中。 请你采取最优策略分割 s ,使剩下的字符 最少 。 示例 1: 输入 :s =

    2024年01月16日
    浏览(48)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包