Given a string s and a non-empty string p, find all the start indices of p's anagrams in s. Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100. The order of output does not matter. Example 1: Input: s: "cbaebabacd" p: "abc" Output: [0, 6] Explanation: The substring with start index = 0 is "cba", which is an anagram of "abc". The substring with start index = 6 is "bac", which is an anagram of "abc". Example 2: Input: s: "abab" p: "ab" Output: [0, 1, 2] Explanation: The substring with start index = 0 is "ab", which is an anagram of "ab". The substring with start index = 1 is "ba", which is an anagram of "ab". The substring with start index = 2 is "ab", which is an anagram of "ab".
这道题先想到了用hash存一下p出现的字符和个数,然后遍历s来做。但超时了:
class Solution { public List<Integer> findAnagrams(String s, String p) { List<Integer> resList = new ArrayList<>(); if(s == null || p == null){ return resList; } Map<Character, Integer> map =new HashMap<>(); for(char c : p.toCharArray()){ map.put(c, map.getOrDefault(c,0)+1); } for(int i = 0; i <= s.length()-p.length(); i++){ Map<Character, Integer> temp =new HashMap<>(map); for(int j = i; j < i + p.length(); j++){ char c = s.charAt(j); if(temp.containsKey(c)){ int v = temp.get(c) - 1; if(v < 0){ break; } temp.put(c,v); } else{break;} } boolean match = true; for(char c : temp.keySet()){ if(temp.get(c) != 0){ match = false; break; } } if(match){ resList.add(i); } } return resList; } }
第一个就是定义的hash数组长度是256,因为ascii码的长度是256位的,所以每一位的索引表示一个字符的计数值。另外就是在窗口的部分,分别用两个指针表示窗口的左边界和右边界。还有用count作为一个计数值,它的含义是窗口里面的字符串和p字符串的相差的字符个数。
在窗口右移的过程中,如果这个位置上面的hash值是大于0的,那么,说明我当前这个位置的字符增加之后可以使得窗口和p字符串的相异度减小一个;然后判断如果count为0,即相异距离为0的时候,记录下左边界的位置,并加入到结果集合里面。接着就是移动左边界的指针,首先需要判断窗口长度等于p字符串的长度的时候才开始移动边界。移动做边界的时候,同样如果这个位置的hash值是非负的,才将count进行加一,包括在上面,如果加入了一个字符,使得hash值对应项是负值,则count值不会变化。
最终,当右边界移动完成之后,则返回所有的满足条件时窗口左索引的集合。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> list = new ArrayList<>();
int[] map = new int[256];
for(char c : p.toCharArray()){
map[c]++;
}
int left = 0, right = 0, count = p.length();
while(right < s.length()){
char r = s.charAt(right);
char l = s.charAt(left);
if(map[r] >= 1){ //窗口右移;相应的hash值减小;如果这个位置的Hash值是正的,表示p字符串也包含这个,所以count做减法
count--;
}
map[r]--;
right++;
if(count == 0){ //count指示器,为0表示和p对应的hash值完全一致
list.add(left);
}
//move
if(right - left == p.length()){//如果当窗口大小一定的时候即窗口大小和需要比较的字符串大小一致的时候,将窗口左边的指针向右边移动,移动的同时左边的字符计数因为在第一个if的地方hash值减小过,所以需要执行对应恢复操作,即:hash值增加,count计数值增加。
if(map[l] >= 0){
count++;
}
map[l]++;
left++;
}
}
return list;
}
}