Leecode-567 字符串的排列

题目描述

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = “ab” s2 = “eidbaooo”
输出: True
解释: s2 包含 s1 的排列之一 (“ba”).

示例2:
输入: s1= “ab” s2 = “eidboaoo”
输出: False

思路

第一眼看到这个题 暴力解法在脑海里面就散开了
直接罗列s1的所有字符的排列组合,然后每个组合都去跟s2匹配,看看是否在s2中存在。
只要有一个存在,就返回true
然后就想 怎么能够确保罗列完所有的组合呢 ,然后卡住了。。。。。

后面实在不知道咋做,就开始看答案。发现了 ”滑动窗口“ 解法
还要想明白一个事:

重点

不再是去举例s1的每一种组合可能,而是判断s2的一个序列,如果该子序列中 每个字符的数量与s1中的字符数量相同,那么就代表符合题意。

  • 例: s1:ab s2:bacd
    那么s2的 ”ba" 子序列 统计字符数量为 a:1, b:1, s1中的字符数量统计为:a:1, b:1
    这就是 **s2包含s1的排列

现在要做的就是 从s2的最左边,保持一个s1长度的窗口,每次+1,依次滑动到s2的最后,只要滑动过程中出现子序列,与s1的字符统计数量完全相同,即代表找到了符合题意的s2子序列 ,返回true

选择数据结构:
  • hashMap
    统计字符数量,一般想到的肯定是map,直接两个hashMap,第一个map1用来存储s1的字符以及各字符的数量,第二次map2用来存储s2每次滑动窗口所获得的子序列的字符以及各字符的数量。
    那么代码就是下面这个样子:
public boolean checkInclusion(String s1, String s2) {
        int n1 = s1.length(), n2 = s2.length();
        if(n1 > n2){
            return false;
        }
        Map<Character, Integer> map1 = new HashMap<>();
        Map<Character, Integer> map2 = new HashMap<>();
        for(int i = 0; i < n1; i++){
            if(map1.containsKey(s1.charAt(i))){
                map1.put(s1.charAt(i), map1.get(s1.charAt(i)) + 1);
            }else{
                map1.put(s1.charAt(i), 1);
            }
            if(map2.containsKey(s2.charAt(i))){
                map2.put(s2.charAt(i), map2.get(s2.charAt(i)) + 1);
            }else{
                map2.put(s2.charAt(i), 1);
            }
        }
        if(map1.equals(map2)){
            return true;
        }else{
            for(int i = 0; i < n2-n1; i++){
                map2.put(s2.charAt(i), map2.get(s2.charAt(i)) - 1);
                if(map2.get(s2.charAt(i)) < 1){
                    map2.remove(s2.charAt(i));
                }
                if(map2.containsKey(s2.charAt(i + n1))){
                    map2.put(s2.charAt(i + n1), map2.get(s2.charAt(i + n1)) + 1);
                }else{
                    map2.put(s2.charAt(i + n1), 1);
                }
                if(map1.equals(map2)){
                    return true;
                }
            }
            return false;
        }        
    }

作者:dixinfan-2
链接:https://leetcode-cn.com/problems/permutation-in-string/solution/java-shuang-hashmaphua-dong-chuang-kou-by-dixinfan/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这里我用的别人的答案,可以看出,逻辑很清楚,但是就是代码十分冗长,但是由于使用了两个hashMap,所以空间复杂度比较高。

  • 数组
    第二种解法就是利用数组来存储每个字符的数量:具体解释可以在代码中看到
	public static boolean checkInclusion(String s1, String s2) {
        int len1 = s1.length();
        int len2 = s2.length();
        if(len1 > len2)
            return false;
        int[] array1 = new int[26];
        int[] array2 = new int[26];
        for(int i = 0; i < len1; i++){
            array1[s1.charAt(i) - 'a']++;
            array2[s2.charAt(i) - 'a']++;
        }

        for(int i = len1 ; i < len2; i++) {
            if(isEqual(array1, array2))
                return true;
            //滑动窗口, ++是将所滑过的字母对应的数组中的元素+1,
            //--是当数组滑动过了当前的位置,并没有找到与s1完全相同的序列。
            // 所以需要删除前面添加进数组中的元素(为了保持整个数组完全相同)
            /**
             *例子:s1:ab   s2: cdabef  经过第一个for循环  array1={1,1,0,0,.....} array2={0,0,1,1,0,0,....}
             * 1.滑动窗口第一次进入s2,数组array2变为{1,0,0,1,0,0,.....}   (s2 中第一个字符'c'对应的数组元素变为0)
             * 此时进入下一次循环,判断array1 array2不相等,继续
             * 2.滑动窗口滑动一个位置,保持len1的长度不变,  数组array2变为{1,1,0,0,0,....} (s2中第二个字符'd'对应的数组元素变更为0)
             * 此时进入下一次循环,判断array1,array2相等,返回true
             * (i-len1) 是为了维护窗口左边界, i是为了维护窗口右边界   每一次滑动数组不相等,则滑动一次窗口,左边界+1,右边界也+1.
             */
            --array2[s2.charAt(i - len1) - 'a'];
            ++array2[s2.charAt(i) - 'a'];

        }
        return isEqual(array1, array2);
    }
    private static boolean isEqual(int[] array1, int[] array2) {
        for(int i = 0; i < 26; i++){
            if(array1[i] != array2[i])
                return false;
        }
        return true;
    }

可以看出第二个办法的空间复杂度一下就降到了o(1) 。

这道题学到了很多,比如 滑动窗口解法 还有就是尽量利用简单的数据结构来解决问题,优化不是一点半点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值