438.Find All Anagrams in a String
Tags: [sliding_window], [map]
Link: https://leetcode.com/problems/find-all-anagrams-in-a-string/?tab=Description
Given a stringsand anon-emptystringp, find all the start indices ofp's anagrams ins.
Strings consists of lowercase English letters only and the length of both stringssandpwill 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".
Solution: sliding window, map
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
if not s or not p or len(s) < len(p):
return []
p_map = {}
for c in p:
if c in p_map:
p_map[c] += 1
else:
p_map[c] = 1
moving_window = {}
for i in xrange(len(p)):
c = s[i]
if c in moving_window:
moving_window[c] += 1
else:
moving_window[c] = 1
result = []
for start in xrange(len(s) - len(p) + 1):
if self.are_equal_maps(p_map, moving_window):
result.append(start)
end = start + len(p) - 1
# update moving window
start_c = s[start]
moving_window[start_c] -= 1
if not moving_window[start_c]:
del moving_window[start_c]
if end + 1 >= len(s):
break
next_end_c = s[end + 1]
if next_end_c in moving_window:
moving_window[next_end_c] += 1
else:
moving_window[next_end_c] = 1
return result
def are_equal_maps(self, map1, map2):
if not map1:
return not map2
if not map2:
return not map1
if len(map1) != len(map2):
return False
for k1 in map1:
if k1 not in map2 or map1[k1] != map2[k1]:
return False
return True
Revelation:
- We don't need to rebuild map each time, we can use sliding_window map, the pull start element out, and push the next end element into the 'window'.
Note:
- Time complexity = O(n), n is the number of the characters of the given string s.
Time Limited Exceeded
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
if not s or len(s) < len(p):
return []
p_map = {}
for c in p:
if c in p_map:
p_map[c] += 1
else:
p_map[c] = 1
s_len = len(s)
start = 0
end = 0
result = []
tmp_p_map = dict(p_map)
while end < s_len:
if not tmp_p_map:
result.append(start)
start += 1
end = start
tmp_p_map = dict(p_map)
else:
curr = s[end]
if curr not in tmp_p_map:
start += 1
end = start
tmp_p_map = dict(p_map)
else:
tmp_p_map[curr] -= 1
if not tmp_p_map[curr]:
del tmp_p_map[curr]
end += 1
if not tmp_p_map:
result.append(start)
return result
Note:
- Time complexity = O(n * m), n is the length of the string s, and m is the length of the string p.
Time Limited Exceeded
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
if not s or len(s) < len(p):
return []
# do counting chars on p
p_map = self.counting(p)
result = []
s_len = len(s)
p_len = len(p)
for start in xrange(s_len - p_len + 1):
sub_s = s[start:start + p_len]
sub_s_map = self.counting(sub_s)
if self.are_equal_maps(p_map, sub_s_map):
result.append(start)
return result
def counting(self, s):
c_map = {}
if not s:
return c_map
for c in s:
if c in c_map:
c_map[c] += 1
else:
c_map[c] = 1
return c_map
def are_equal_maps(self, map1, map2):
if not map1:
return not map2
if not map2:
return not map1
if len(map1) != len(map2):
return False
for k1 in map1:
if k1 not in map2 or map1[k1] != map2[k1]:
return False
return True
Note:
- Time complexity = O(n * m), n is the length of the string s, and m is the length of the string p.