力扣刷题第438题. 找到字符串中所有字母异位词:哈希表,滑动数组

438. 找到字符串中所有字母异位词1

力扣

438. 找到字符串中所有字母异位词

难度中等643

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

 示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • s 和 p 仅包含小写字母


经验总结:

1:技巧方法方面 

通过本题,在比较二十六个英文字母时候 开辟计数数组count是比较方便的,给出一个字符串统计其中的字符出现个数,其实是比较好操作的,因为数字 0 - 9 十个 ,大小写 各自26个,这些全部加起来,也还是常数级内进行操作。。。用到的时间和空间复杂度都为O(1)

但是有时候数字他不给出限制 大小,自己做数组,用下标存储,浪费太大了

本题考察的思想,关于哈希表方面的,其实无非就是 用数组做个哈希表,存放小写字母出现的个数,这些在考察字母异位词,赎金信问题中,打的一拳开,免得百拳来。

2:其次,关于滑动窗口,以前对滑动窗口的理解还是不够深,如本题为什么人家的方法能比我的快,用时10ms以内,而我的虽然用暴力安慰奖超过百分之五,用时两千毫秒,再用“滑动数组”依然是百分之十,用时一千毫秒呢?仔细分析,原因莫不是我的“滑动窗口” 是真假美猴王。

我没有掌握理解滑动窗口的思想。 仔细分析大佬的题解,每次滑动窗口 大佬的·题解是:

最核心的一点,大佬的思想是:

因为cntS这个计数数组本质上就是记录一定长度内子串的每个字符对应的小写字母出现的个数。

所以每次就通过左边的字母出现的次数 减一

右边窗口新增进来的字母的次数 加一 进行重复利用的操作 。一旦P字符串长度数十万,S字符串只用读取一次十万 统计出现个数,之后每次加一减一。

滑动窗口真可谓是YYDS了!

至此才算是初窥门径,略懂皮毛。算法思想博大精深!

而我的所谓“双指针”“滑动数组”,其实无非就是,位置的加一减一,变更位置后,还是对符合条件的数组重新读取,再次操作,存入cntS这个统计S字符串中 长度等于P字符串的子串中的各个字符出现的个数。实在是粗陋浅薄,不堪入目~~~

真&滑动窗口



找到字符串中所有字母异位词
提交记录
61 / 61 个通过测试用例
状态:通过
执行用时: 10 ms
内存消耗: 39.1 MB

邀请好友来挑战 找到字符串中所有字母异位词

语言: java

添加备注


class Solution 
{
    public List<Integer> findAnagrams(String s, String p) 
    {
        //首先变量命名不要给自己找麻烦 这种Oj题目大小写容易分辨不出来
        //其次三种情况

        int slen = s.length();
        int plen = p.length();
        List <Integer> list = new ArrayList<Integer> ();
        if(slen < plen)
        {
            return list;
        }

        int [] scnt = new int[26];
        int [] pcnt = new int [26];
        for(int i = 0; i < plen ; i++)
        {
            scnt[s.charAt(i)-'a']++;
            pcnt[p.charAt(i)-'a']++;
        }
        if(Arrays.equals(scnt,pcnt))
        list.add(0);

        for(int i = plen;i< slen ; i++)
        {
            scnt[s.charAt(i-plen)-'a']--;
            scnt[s.charAt(i)-'a']++;
            if(Arrays.equals(scnt,pcnt))
            {
                list.add(i-plen+1);
            }
        }

        return list;
    }
}

假&滑动窗口

class Solution 
{
    public List<Integer> findAnagrams(String s, String p) 
    {
        int sLen = s.length();
        int pLen = p.length();
        List <Integer> list = new ArrayList <Integer>();
        int left = 0;
        int right = 0;
        if(sLen<pLen)
        {
            return list;
        }

        while(right < sLen)
        {
            if(right-left+1 == pLen)
            {
                int[] cntP = new int[26];
                int[] cntS = new int[26];
                for(int i=0;i<pLen;i++)
                {
                    cntP[p.charAt(i)-'a']++;
                }
                for(int i=left;i<left+pLen;i++)
                {
                    cntS[s.charAt(i)-'a']++;
                }
                if(Arrays.equals(cntP,cntS))
                {
                    list.add(left);
                }
                left++;
            }
        right++;
        }

        return list;

    }
}

上述仅为针对本题的 滑动窗口 思想 

下面补充 滑动窗口的相关

细节知识点 

首先:滑动窗口,窗口根据右边的动,我在编程过程中,考虑过 如 p字符串长度为3,而s字符串长度为7时候,则在右边right滑动到  下标为6,第七位的时候就结束,但是如果 

s=abcdefabc

p=abc,那就直接漏了一种情况。直接破防 这是右边的一种。

滑动窗口,本质上是双指针,还有快慢指针,这种双指针的情况其实盯着快慢指针中的快指针,

滑动窗口中的右边栏,切记滑动窗口右边栏一定要滑动到底~!

正如二分查找变种题的精髓在于,不管他是有序还是无序,用左右指针确定他的范围!

所有的滑动窗口问题,如果能从暴力法优化的角度进行考虑和剪枝,都不难得到。

滑动窗口法的特点是,一连串元素的信息,可以用常数时间推断出,该串左减去右加上“整体移位”之后,得到新串的信息,一句话总结本题的滑动窗口,其实有点类似dp动态规划,已经操作过的,不再重复操作,记录下来重复用。

其次,像本题,找到右指针后,一定要探到底,找到循环不变量后。

还要注意,如果是for循环,只要从0开始起手,探到length-1索引下标到底,再加上for循环的语句三,是代码块执行之后再自加操作,基本不会漏情况

本题如果是while循环,如果把right++;放在代码块前。

right-left+1 == lengthOfStringP这种情况,如果字符串p是一,返回结果为空

所以大多数滑动窗口用for


Last , 两个数组进行比较的equals这个静态方法真的很香~!

注意比较两个数组必须是相同类型哦~~

boolean  isTheSame = Arrays.equals(nums1,num2);


总结

滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

可以发现滑动窗口的精妙之处在于根据当前子序列子字符串的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。

滑动窗口,本质上是双指针,还有快慢指针,这种双指针的情况其实盯着快慢指针中的快指针,

滑动窗口中的右边栏,切记滑动窗口右边栏一定要滑动到底~!

正如二分查找变种题的精髓在于,不管他是有序还是无序,用左右指针确定他的范围!

所有的滑动窗口问题,如果能从暴力法优化的角度进行考虑和剪枝,都不难得到。

滑动窗口法的特点是,一连串元素的信息,可以用常数时间推断出,该串左减去右加上“整体移位”之后,得到新串的信息,一句话总结本题的滑动窗口,其实有点类似dp动态规划,已经操作过的,不再重复操作,记录下来重复用。


由于本人主要发布此贴用来总结以及后续复习,水平有限,文章中难免有不妥之处,万望海涵。不当之处还请拨冗指正。我们一起学习,共同进步!


后续动态规划刷题:

#学而不思则罔,思而不学则殆!

日拱一卒!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

傻根根呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值