【Leetcode 热题 100】438. 找到字符串中所有字母异位词

问题背景

给定两个字符串 sssppp,找到 sss 中所有 ppp 的异位词(字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语,并使用所有原字母一次。 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

数据约束

  • 1≤s.length,p.length≤3×1041 \le s.length, p.length \le 3 \times 10 ^ 41s.length,p.length3×104
  • sssppp 仅包含小写字母

解题过程

字母异位词的特征是两个字符串所包含的字母种类和数量相同,想到用哈希表。数据约束中明确说明了字符串只包含小写字母,那么开大小为 262626 的数组就行。
这里要找两个不同字符串中包含的字母异位词,那给它们分别定义一张哈希表,在长度相同的情况下每次都去比对这两张表中的数据是否一致即可。
考虑到比对的过程消耗的时间与字符串的长度无关,所以时间复杂度可以看作是 O(1)O(1)O(1),综合的时间复杂度是 O(N)O(N)O(N),其中 NNN 表示字符串 sss 的长度。

进一步来说,如果在滑动窗口的题目中,出现要定义两张哈希表进行比对的情况,可以尝试将哈希表定义为两个指标之差来优化掉一张表。在本题中,可以定义一个 diffdiffdiff 数组,表示两个字符串中对应字符数量之差。
这种做法仍然需要比对哈希表,在各个方面没有数量级上的优化。优化了,但是没有完全优化。

在此基础之上,可以再定义一个变量来记录不同指标的数量,用它与 000 的关系来判断是否符合条件。在本题中定义 diffCountdiffCountdiffCount 变量表示待比较的两个字符串中数量不同的字符数。这样一来可以优化掉比对哈希表的过程,但最终还是没有数量级上的优化。

具体实现

比对双哈希表

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        char[] chS = s.toCharArray();
        int n = chS.length, m = p.length();
        // 如果待查找的字符串反而更短,那没有答案,直接返回
        if(n < m) {
            return res;
        }
        int[] mapS = new int[26], mapP = new int[26];
        for(char c : p.toCharArray()) {
            mapP[c - 'a']++;
        }
        for(int i = 0; i < n; i++) {
            mapS[chS[i] - 'a']++;
            if(i < m - 1) {
                continue;
            }
            if(check(mapS, mapP)) {
                res.add(i - m + 1);
            }
            mapS[chS[i - m + 1] - 'a']--;
        }
        return res;
    }

    private boolean check(int[] mapS, int[] mapP) {
        for(int i = 0; i < 26; i++) {
            if(mapS[i] != mapP[i]) {
                return false;
            }
        }
        return true;
    }
}

比对单哈希表

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        char[] chS = s.toCharArray();
        int n = chS.length, m = p.length();
        if(n < m) {
            return res;
        }
        int[] diff = new int[26];
        for(char c : p.toCharArray()) {
            diff[c - 'a']++;
        }
        for(int i = 0; i < n; i++) {
            diff[chS[i] - 'a']--;
            if(i < m - 1) {
                continue;
            }
            if(check(diff)) {
                res.add(i - m + 1);
            }
            diff[chS[i - m + 1] - 'a']++;
        }
        return res;
    }

    private boolean check(int[] diff) {
        for(int item : diff) {
            if(item != 0) {
                return false;
            }
        }
        return true;
    }
}

检查不同的次数

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        char[] chS = s.toCharArray();
        int n = chS.length, m = p.length();
        if(n < m) {
            return res;
        }
        int[] diff = new int[26];
        int diffCount = 0;
        for(char c : p.toCharArray()) {
            // 当前字符由 0 个变为 1 个,数量不同的字符数增加
            if(diff[c - 'a']++ == 0) {
                diffCount++;
            }
        }
        for(int i = 0; i < n; i++) {
            // 当前字符由 1 个变为 0 个,数量不同的字符数减少
            if(--diff[chS[i] - 'a'] == 0) {
                diffCount--;
            }
            if(i < m - 1) {
                continue;
            }
            // 没有数量不同的字符数,符合题目要求,记录答案
            if(diffCount == 0) {
                res.add(i - m + 1);
            }
            // 当前字符由 0 个变为 1 个,数量不同的字符数增加
            if(diff[chS[i - m + 1] - 'a']++ == 0) {
                diffCount++;
            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值