力扣-438. 找到字符串中所有字母异位词(中等)(思路)(滑动窗口法)java

题目描述:

给定两个字符串 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 仅包含小写字母

思路:

最简单的思路是用数组下标存起来,然后用s一个个去比较p。如下

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int a[] = new int[26];
        int t[] = new int[26];
        List<Integer> result = new LinkedList<>();
        for(int i = 0 ; i < p.length() ; i++)a[p.charAt(i) - 'a']++;

        for(int i = 0; i < s.length() ; i++)
        {
            if(a[s.charAt(i)-'a'] > 0 && (i+p.length()) <= s.length())//先看有没有这个字符,然后长度会不会越界
            {
                int flag = 0;
                int j,k;
                Arrays.fill(t, 0);
                for(j = i,k=i+p.length()-1; k>=j; j++,k--)
                {
                    t[s.charAt(j)-'a']++;
                    t[s.charAt(k)-'a']++;
                    if(k==j)t[s.charAt(j)-'a']--;
                    if(t[s.charAt(j)-'a'] > a[s.charAt(j)-'a'] || t[s.charAt(k)-'a'] > a[s.charAt(k)-'a'])break;
                }
                for(j = 0 ; j < 26 ; j++)
                {
                    if(a[j] != t[j])
                    {
                        flag = 1;
                        break;
                    }
                }
                if(flag == 0) result.add(i);
            }
        }
        return result;
    }
}

什么情况下会想到滑动窗口法:

任何题目如果没有思路其实都可以想一下暴力解法。这道题暴力解法思路简单:

遍历任意i,j,使得i和j之间的子串长度,等于p串的长度。该子串称之为x。该步复杂度为O(n)。
判断x是否与p是异位词。是的话,则把i加入答案中。该步复杂度为O(n)。
暴力法的复杂度为O(n^2)。显然不高效。

可以发现第二步其实做了很多不必要的操作,例如[i, j]和[i+1, j+1]两个子串在暴力法第二步中,需要各遍历一次,完全没必要。其实[i+1, j+1]完全可以在[i, j]的基础上做判断,也就是去掉头部的字符(i位置),加上尾部的字符(j+1位置)。这样第一步的复杂度可以降到O(1)。整体复杂度降到O(n)。已经得到信息不重复使用就浪费了,没必要重新搜集近乎相同的信息。这就是滑动窗口法。

滑动窗口法的特点是,一连串元素的信息,可以用常数时间推断出,该串整体移位后,新串信息。

所有滑动窗口问题,如果能从暴力法优化的角度思考,都不难想到,代码如下:

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

你可能感兴趣的:(leetcode,贪心算法,算法)