给定两个字符串 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,可以逐个比较两个数组的元素,这个方法较为有用,大家可以记忆一下
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;
}