240919-438找到字符串中所有字母异位词(固定窗口大小)

51 阅读2分钟

给定两个字符串 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" 的异位词。

初版(冗余,错误版本)

试图(right - left + 1) == p.length()时候,判断窗口字符,并且左指针收缩1为下次右指针右移做准备,但貌似哪里出了问题,有的题解过不去

public List<Integer> findAnagrams(String s, String p) {

    //直接想法是穷举所有p.length的字串来找出符合条件的起始索引
    //因为连续的特性——想到了优化办法滑动窗口
    char[] base = new char[128];
    char[] windows = new char[128];
    int left = 0;
    int right = 0;
    for (int i = 0; i < p.length(); i++) {
        base[p.charAt(i)]++;
    }
    List<Integer> res = new ArrayList<>();
    int hits = 0;

    while(right < s.length()){
        if (windows[s.charAt(right)] < base[s.charAt(right)]){
            hits++;
        }
        windows[s.charAt(right)]++;
        if ((right - left + 1) == p.length()){
            if (hits == p.length()){
                System.out.println("left: " + left + " right: " + right);
                res.add(left);
            }
            //左指针右移1,为下一轮右指针右移做准备
            if (base[s.charAt(left)] > 0 && windows[s.charAt(left)] == base[s.charAt(left)]){
                hits--;
            }
            windows[s.charAt(left)]--;
            left++;
        }
        right++;
    }
    return res;
}

优化——固定大小的窗口

因为异位词长度必定是p.length,所以我们只考虑长度为p.length的滑动窗口,初始化时候即让右指针指向p.length-1,并判断【0-p.length-1】是否符合异位词,接下来循环中每次都右指针加1,左指针+1判断所有长度为p.length的字符串即可
这里用到了java.util.Arrays;的内置方法equals,可以逐个比较两个数组的元素,这个方法较为有用,大家可以记忆一下

image.png

public List<Integer> findAnagrams(String s, String p) {
    if (p.length() > s.length())
        return new ArrayList<>();
    //直接想法是穷举所有p.length的字串来找出符合条件的起始索引
    //因为连续的特性——想到了优化办法滑动窗口
    char[] base = new char[128];
    char[] windows = new char[128];
    int left = 0;
    int right = p.length() - 1;
    for (int i = 0; i < p.length(); i++) {
        base[p.charAt(i)]++;
        windows[s.charAt(i)]++;
    }
    List<Integer> res = new ArrayList<>();

    if (Arrays.equals(base, windows)){
        res.add(0);
    }
    while(right < s.length() - 1){
        windows[s.charAt(++right)]++;
        windows[s.charAt(left++)]--;
        if (Arrays.equals(base, windows)){
            res.add(left);
        }
    }

    return res;
}