秋招 - 算法

53 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情 >>

一、题目描述 LeetCode - 438

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

二、解题思路

本题要求找到字符串中所有的异位词子串,我们可以考虑使用滑动窗口来解决。定义与字符串p长度相等的窗口,在字符串s上进行移动,判断窗口内字母出现的次数,若字母出现的次数等于字符串p中字母出现的次数,则说明窗口内的子串为p的一个异位词,将窗口起始下标加入数组内,并将窗口向后移动。在具体的实现时,我们可以使用数组来统计字符个数,既可以将当前字符减'a'得到的值作为数组的索引,并对这个索引对应的值加1,进行统计。

三、代码

public List<Integer> findAnagrams(String s, String p) {
    int sl = s.length(), pl = p.length();

    if (sl < pl) {
        return new ArrayList<Integer>();
    }

    List<Integer> ans = new ArrayList<Integer>();
    int[] sslic = new int[26];
    int[] pslic = new int[26];
    for (int i = 0; i < pl; ++i) {
        ++sslic[s.charAt(i) - 'a'];
        ++pslic[p.charAt(i) - 'a'];
    }

    if (Arrays.equals(sslic, pslic)) {
        ans.add(0);
    }

    for (int i = 0; i < sl - pl; ++i) {
        --sslic[s.charAt(i) - 'a'];
        ++sslic[s.charAt(i + pl) - 'a'];

        if (Arrays.equals(sslic, pslic)) {
            ans.add(i + 1);
        }
    }
    return ans;
}

五、总结

本题通过构造辅助数组完成对字符的统计,并与目标字符串进行比较,从而找出所有的异位词。时间复杂度为O(m+(n-m)*S),其中S为所有字符的个数26,空间复杂度为O(S)。