思路:使用滑动窗口 ,设置一个大小为p.length大小的窗口,窗口中的字符出现的次数用一个int类型的数组记录,数组的下标代表字符ASCII码,一个字符每出现一次就让int[字符对应的ascii] +1,当滑动窗口向右移动时,只需要更新左边移出去和右边移进来的两个字符。
代码如下:
public static List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>();
int pLength = p.length();
//对p字符串中所有出现的字符数进行计数,这里使用字符的ASCII码作为int数组的下标
//如果a(ASCII为97)出现一次则 int[92]就+1;
int[] ascii = new int[123];
char[] chars = p.toCharArray();
for (int i = 0;i<chars.length;i++) {
int index = chars[i];
ascii[index]+=1;
}
int[] resource = new int[123];
for (int i =0;i<=s.length()-pLength;i++){
int k =97;
if (i==0){//对首个字符串中的字符数量标记
for (int j =i;j<i+pLength;j++){
int index = s.charAt(j);
resource[index]+=1;
}
}else { //这里是窗口右移一个位置,删除最左侧有个字符移出去,最右侧有个字符移动进来,更新这两个字符的出现次数
int a =s.charAt(i-1),b=s.charAt(i+pLength-1);
resource[a]-=1;
resource [b]+=1;
}
//判断两个字符串中的每个字符的数量是否相等
while(k<123){
if (ascii[k]==resource[k]) {
k++;
continue;
}
else break;
}
if (k==123) result.add(i);
}
return result;
}
更优雅的写法:
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<Integer>();
}
List<Integer> ans = new ArrayList<Integer>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < pLen; ++i) {
++sCount[s.charAt(i) - 'a'];
++pCount[p.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;
}