给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
- 字母异位词指字母相同,但排列不同的字符串。
- 不考虑答案输出的顺序。
示例 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" 的字母异位词。
“双指针 + 滑动窗口”求解。定义滑动窗口的左右两个指针 left、right,right 一步步往右走遍历 s 字符串,当 right 指针遍历到的字符加入 t 后超过 counter 的字符数量时,将滑动窗口左侧字符不断弹出,也就是 left 指针不断右移,直到符合要求为止。
若滑动窗口长度等于字符串 p 的长度时,这时的 s 子字符串就是 p 的异位词。
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
counter = Counter(p)
ans = []
left = right = 0
t = Counter()
while right < len(s):
t[s[right]] += 1
while t[s[right]] > counter[s[right]]:
t[s[left]] -= 1
left += 1
if right - left + 1 == len(p):
ans.append(left)
right += 1
return ans
“暴力”求解。利用哈希表 counter 统计字符串 p 中每个字符出现的次数。然后遍历字符串 s,判断子串 s[i, i + p)
中每个字符出现的次数是否与 counter 相同。若是,则将当前下标 i 添加到结果列表中。时间复杂度 O(s.length * p.length)
。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int[] counter = new int[26];
for (char c : p.toCharArray()) {
++counter[c - 'a'];
}
List<Integer> ans = new ArrayList<>();
for (int i = 0; i + p.length() - 1 < s.length(); ++i) {
int[] t = Arrays.copyOf(counter, counter.length);
boolean find = true;
for (int j = i; j < i + p.length(); ++j) {
if (--t[s.charAt(j) - 'a'] < 0) {
find = false;
break;
}
}
if (find) {
ans.add(i);
}
}
return ans;
}
}
“双指针 + 滑动窗口”求解。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int[] counter = new int[26];
for (char c : p.toCharArray()) {
++counter[c - 'a'];
}
List<Integer> ans = new ArrayList<>();
int left = 0, right = 0;
int[] t = new int[26];
while (right < s.length()) {
int i = s.charAt(right) - 'a';
++t[i];
while (t[i] > counter[i]) {
--t[s.charAt(left) - 'a'];
++left;
}
if (right - left + 1 == p.length()) {
ans.add(left);
}
++right;
}
return ans;
}
}
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> counter(26);
for (char c : p) ++counter[c - 'a'];
vector<int> ans;
int left = 0, right = 0;
vector<int> t(26);
while (right < s.size())
{
int i = s[right] - 'a';
++t[i];
while (t[i] > counter[i])
{
--t[s[left] - 'a'];
++left;
}
if (right - left + 1 == p.size()) ans.push_back(left);
++right;
}
return ans;
}
};
func findAnagrams(s string, p string) []int {
counter := make([]int, 26)
for _, c := range p {
counter[c-'a']++
}
var ans []int
left, right := 0, 0
t := make([]int, 26)
for right < len(s) {
i := s[right] - 'a'
t[i]++
for t[i] > counter[i] {
t[s[left]-'a']--
left++
}
if right-left+1 == len(p) {
ans = append(ans, left)
}
right++
}
return ans
}