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".
Map. O(n)
理解题意:明确anagram的本质是字符的统计频次一致。所以用Map<Character, Integer>统计字符频次。发现频次吻合的时候就是找到anagram的时候。
1.一开始统计一遍对比物p的字符频次,注意按负数统计。
2.开始扫描字符串s。在中间的general case的时候,入新的字符的统计,出最远字符的统计。(这样可以做到O(1)时间而不是p.length()时间)
3.检查是不是抵消吻合让map的size()变0了。(用size()==0来判断也是为了降低时间复杂度,如果维护两个map,每次要靠确认是不是每个char的count都一致的话要浪费O(p.length())时间。
细节:
1.count == 0的时候要remove一下,不然不能保证size()归0。
实现:
class Solution { public List<Integer> findAnagrams(String s, String p) { List<Integer> ans = new ArrayList<>(); Map<Character, Integer> map = new HashMap<>(); char[] cs = s.toCharArray(); for (char c : p.toCharArray()) { map.put(c, map.getOrDefault(c, 0) - 1); } for (int i = 0; i < s.length(); i++) { map.put(cs[i], map.getOrDefault(cs[i], 0) + 1); if (map.get(cs[i]) == 0) { map.remove(cs[i]); } if (i >= p.length()) { map.put(cs[i - p.length()], map.getOrDefault(cs[i - p.length()], 0) - 1); if (map.get(cs[i - p.length()]) == 0) { map.remove(cs[i - p.length()]); } } if (map.size() == 0) { ans.add(i - p.length() + 1); } } return ans; } }