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

传送门

Solution

思路1:

滑动窗口。维护窗口内字符的数量,判断与\(p\)字符串数量是否相等。复杂度\(O((sLen-pLen)*26)\)

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        if (sLen < pLen) {
            return new ArrayList<>();
        }
        List<Integer> ans = new ArrayList<>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; i++) {
            pCount[p.charAt(i) - 'a']++;
            sCount[s.charAt(i) - 'a']++;
        }
        if (Arrays.equals(sCount, pCount)) {
            ans.add(0);
        }
        for (int i = 0; i < sLen - pLen; i++) {
            sCount[s.charAt(i) - 'a']--;
            sCount[s.charAt(i + pLen) - 'a']++;
            if (Arrays.equals(sCount, pCount)) {
                ans.add(i + 1);
            }
        }
        return ans;
    }
}

思路2:

滑动窗口优化。不维护窗口与\(p\)字符串字符的数量,维护他们的差值\(diff\)\(diff\)\(0\),即为异位词。判断左边界和右边界对\(diff\)的贡献。

左边界考虑\(1\)\(0\)

  • 若为\(1\),滑动后\(diff+1\)
  • 若为0,滑动后\(diff-1\)

右边界考虑\(-1\)\(0\)

  • 若为\(-1\),滑动后\(diff+1\)
  • 若为0,滑动后\(diff-1\)
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        if (sLen < pLen) {
            return new ArrayList<>();
        }
        List<Integer> ans = new ArrayList<>();
        int[] count = new int[26];
        for (int i = 0; i < pLen; i++) {
            count[p.charAt(i) - 'a']--;
            count[s.charAt(i) - 'a']++;
        }
        int diff = 0;
        for (int i = 0; i < 26; i++) {
            if (count[i] != 0) {
                diff++;
            }
        }
        if (diff == 0) 
            ans.add(0);
        for (int i = 0; i < sLen - pLen; i++) {
           if (count[s.charAt(i) - 'a'] == 1) {
               diff--;
           } else if (count[s.charAt(i) - 'a'] == 0) {
               diff++;
           }
           count[s.charAt(i) - 'a']--;
           if (count[s.charAt(i + pLen) - 'a'] == -1) {
               diff--;
           } else if (count[s.charAt(i + pLen) - 'a'] == 0) {
               diff++;
           }
           count[s.charAt(i + pLen) - 'a']++;
           if (diff == 0) {
               ans.add(i + 1);
           }
        }
        return ans;
    }
}

思路3:

滑动窗口+双指针。如果\(s\)的某个字符多了,肯定就不符合,所以只能通过不停弹出\(l\)来符合;只有字符不超过\(p\)的字符数量且长度与\(p\)一致,才为答案。

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        if (sLen < pLen) {
            return new ArrayList<>();
        }
        List<Integer> ans = new ArrayList<>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; i++) {
            pCount[p.charAt(i) - 'a']++;
        }
        int l = 0;
        for (int r = 0; r < sLen; r++) {
            int right = s.charAt(r) - 'a';
            sCount[right]++;
            while (sCount[right] > pCount[right]) {
                int left = s.charAt(l) - 'a';
                sCount[left]--;
                l++;
            }
            if (r - l + 1 == pLen) {
                ans.add(l);
            }
        }
        return ans;
    }
}

思路4:

\(p\)看做资源去消耗,能不能把当前\(s\)吃掉,吃掉后\(r++\),有可能长度与\(pLen\)一致,如果吃不掉当前\(s\),那就收回之前的\(l++\),如果出现未出现的字符,加上后也会消耗掉。

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        List<Integer> ans = new ArrayList<>();
        int[] count = new int[26];
        for (int i = 0; i < pLen; i++) {
            count[p.charAt(i) - 'a']++;
        }
        int l = 0, r = 0;
        while (r < sLen) {
            if (count[s.charAt(r) - 'a'] > 0) {
                count[s.charAt(r++) - 'a']--;
                if (r - l == pLen) 
                    ans.add(l);
            } else {
                count[s.charAt(l++) - 'a']++;
            }
        }
        return ans;
    }
}
埋骨何须桑梓地,人生无处不青山
原文地址:https://www.cnblogs.com/ACMerszl/p/15622326.html