Open azl397985856 opened 3 years ago
分析一下,首先题中说找到 s 中所有是 p 的字母异位词(假如p1 的字母异位词是p2, 两个字符串p1、p2包含的各种字母数量都相等)的子串,就这句话,就包含了如下两个重要信息:
本题,我们可以用固定宽度的滑动窗口来做。
那么用什么数据结构来记录窗口中各个字母出现的次数呢?哈希表是比较一个比较符合直觉的想法。
还有一种就是用数组。因为字符串中只包含小写字母,也就是只有 26 种字母,所以我们可以使用一个长度为 26 的数组来记录,数组下标表示字母,值则表示字母出现的次数。
实现语言: C++
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res; /* 存放这些子串的起始索引startPos */
int restCount = p.size(); /* 维护一个固定长度的窗口, 窗口长度 = len(p), restCount: 剩下的待匹配字符的数量 */
vector<int> pFreq(26); /* 模拟一个计数哈希表 */
for (int i = 0; i < p.size(); i++)
pFreq[p[i] - 'a']++;
vector<int> sFreq(26); /* 模拟一个计数哈希表, sFreq: 字符串s中处于滑动窗口内的字符的频次 */
for (int i = 0; i < s.size(); i++)
{
char c = s[i];
sFreq[c - 'a']++; /* 向窗口末尾加入1个新字符 */
if (sFreq[c - 'a'] <= pFreq[c - 'a']) /* 当前窗口中这个字符的数量还没到上限, 可以成功加入 */
restCount--;
if (i >= p.size()) /* 删除原窗口最前面的那1个字符, 最新的startPos = i - len(p) + 1, 那么上一次的startPos = i - p.size() */
{
char h = s[i - p.size()]; /* h: 上一轮中窗口中最前面的字符 */
sFreq[h - 'a']--;
if (sFreq[h - 'a'] < pFreq[h - 'a'])
restCount++;
}
// 字符都匹配完成, 表示是一个有效的字母异位词(anagram)
if (restCount == 0) /* i - startPos + 1 = len(p) => startPos = i - len(p) + 1 */
res.push_back(i - p.size() + 1);
}
return res;
}
};
学习官方题解
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
target = collections.Counter(p)
ans = []
for i in range(len(s)):
if i >= len(p):
target[s[i - len(p)]] += 1
if target[s[i - len(p)]] == 0:
del target[s[i - len(p)]]
target[s[i]] -= 1
if target[s[i]] == 0:
del target[s[i]]
if len(target) == 0:
ans.append(i - len(p) + 1)
return ans
复杂度分析
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>();
int[] pattern = new int[26];
for (char c : p.toCharArray()){
pattern[c - 'a']++;
}
int[] window = new int[26];
int pLen = p.length();
String patternStr = Arrays.toString(pattern);
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
window[c - 'a']++;
if (i - pLen >= 0) window[s.charAt(i - pLen) - 'a']--;
if (i - pLen + 1 >= 0){
if (Arrays.toString(window).equals(patternStr)){
result.add(i - pLen + 1);
}
}
}
return result;
}
}
Time Complexity: O(n + m) => n : the length of s, m : the length of p Space Complexity: O(1)
Sliding window + Hash table. The window's size is the length of pattern string. Increase the left side and right side of the sliding window by1 at every step. Then check whether the new window's substring has same char frequency as pattern string.
from collections import Counter
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
p_count = Counter(p)
cur_count = Counter()
l, r = 0, 0
ans = []
if len(p) > len(s):
return ans
while r < len(p):
if s[r] in p_count:
cur_count[s[r]] = cur_count.get(s[r], 0) + 1
r += 1
if cur_count == p_count:
ans.append(l)
while r < len(s):
if s[r] in p_count:
cur_count[s[r]] = cur_count.get(s[r], 0) + 1
r += 1
if s[l] in p_count:
cur_count[s[l]] -= 1
l += 1
if cur_count == p_count:
ans.append(l)
return ans
Time: O(N). N is length of s + length of p. We pass s and p by once. The comparison between two counters is O(26), since it only has 26 distinct lowercase letters Space: O(1). The max length of counter is O(26), same reason as its time complexity.
滑动窗口 + 数组模拟哈希表。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
if (s.size() < p.size()) return res;
vector<int> slideWindowS(26);
vector<int> slideWindowP(26);
for (int i = 0; i < p.size(); i++) slideWindowP[p[i] - 'a']++;
int left = 0;
for (int right = 0; right < s.size(); right++) {
slideWindowS[s[right] - 'a']++;
if (right >= p.size()) slideWindowS[s[left++] - 'a']--;
if (slideWindowS == slideWindowP) res.push_back(left);
}
return res;
}
};
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
Map<Character, Integer> map = new HashMap<>();
for (char c: p.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
int left = 0;
int right = 0;
for (; right < s.length(); right++) {
char l = s.charAt(left);
char r = s.charAt(right);
if (right >= p.length()) {
if (map.containsKey(l)) map.put(l, map.get(l) + 1);
else map.put(l, 1);
if (map.get(l) == 0) map.remove(l);
left++;
}
if (map.containsKey(r)) map.put(r, map.get(r) - 1);
else map.put(r, -1);
if (map.get(r) == 0) map.remove(r);
if (map.size() == 0) ans.add(left);
}
return ans;
}
}
思路: 固定长度的滑动窗口 + 辅助数组或hash
复杂度分析:
代码 (C++):
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
if (s.length() < p.length()) return {};
vector<int> dictP(26, 0);
vector<int> dictA(26, 0);
for (auto c : p)
dictP[c - 'a']++;
vector<int> res;
int k = p.length();
for (int i = 0; i < s.length(); ++i) {
if (i - k >= 0) { // fixed length = k sliding windows [0, k - 1]
dictA[s[i - k] - 'a']--;
}
dictA[s[i] - 'a']++;
if (dictA == dictP)
res.push_back(i - k + 1);
}
return res;
}
};
Sliding window.
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
l, N, M = 0, len(s), len(p)
counter = Counter()
cs = Counter(p)
ret = []
k = 0
for r in range(N):
c = s[r]
counter[c] += 1
if counter[c] == cs[c]: k += 1
while r - l + 1 > M:
counter[s[l]] -= 1
if counter[s[l]] == cs[s[l]] - 1:
k -= 1
l += 1
if k == len(cs):
ret.append(l)
return ret
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
res = []
n, m = len(s), len(p)
count = Counter(p)
window = Counter(s[:m - 1])
for i in range(m - 1, n):
window[s[i]] += 1 # add next char to the window
if window == count:
res.append(i - m + 1)
window[s[i - m + 1]] -= 1 # decrease the count of oldest char in the window
if window[s[i - m + 1]] == 0:
del window[s[i - m + 1]]
return res
time complexity: O(n) space complexity: O(1)
固定长度滑动窗口。计数统计窗口内字符个数并且和给定短字符串对比。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int m = s.length();
int n = p.length();
if (m < n) {
return new ArrayList<>();
}
int[] charsInP = new int[26]; // SC: O(1)
for (int i=0; i<n; i++) { // O(n)
charsInP[p.charAt(i) - 'a'] += 1;
}
int[] charsInWindow = new int[26]; // SC: O(1)
for (int i=0; i<n-1; i++) { // O(n)
charsInWindow[s.charAt(i) - 'a'] += 1;
}
List<Integer> ans = new ArrayList<>();
for (int i=n-1; i<m; i++) { // O(m)
// add the char at right index
charsInWindow[s.charAt(i) - 'a'] += 1;
// check
if (isAnagram(charsInP, charsInWindow)) { // O(1)
// left index
ans.add(i - n + 1);
}
// remove the char at left index
charsInWindow[s.charAt(i - n + 1) - 'a'] -= 1;
}
return ans;
}
private boolean isAnagram(int[] chars1, int[] chars2) {
for (int i=0; i<26; i++) {
if (chars1[i] != chars2[i]) {
return false;
}
}
return true;
}
}
TC: O(len(s) + len(p)) SC: O(1)
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> p_cnt(26, 0), s_cnt(26, 0);
for(auto cc: p){
p_cnt[cc - 'a'] ++;
}
int cur_cnt;
vector<int> ans;
int ii, jj;
int last_idx, cur_idx;
int k = p.size();
cur_cnt = 0;
for(ii = 0, jj = 0; ii < s.size(); ++ ii){
if(ii >= k){
last_idx = s[jj] - 'a';
if(p_cnt[last_idx]){
s_cnt[last_idx] --;
if(s_cnt[last_idx] < p_cnt[last_idx]){
-- cur_cnt;
}
}
++ jj;
}
cur_idx = s[ii] - 'a';
if(p_cnt[cur_idx]){
s_cnt[cur_idx] ++;
if(s_cnt[cur_idx] <= p_cnt[cur_idx]){
cur_cnt ++;
}
}
if(cur_cnt == k){
ans.push_back(ii - k + 1);
}
}
return ans;
}
};
n为字符串S长度, k为字符串P长度。
···java
class Solution {
public List
for (char c : p.toCharArray()) {
counts[c - 'a']++;
}
int count = p.length();
for (int l = 0, r = 0; r < s.length(); r++) {
if (counts[s.charAt(r) - 'a']-- > 0) {
count--;
}
while (counts[s.charAt(r) - 'a'] < 0) {
if (counts[s.charAt(l++) - 'a']++ >= 0) count++;
}
if (count == 0) {
list.add(l);
}
}
return list;
}
}
Time: O(n)\
Space: O(1)
Sliding window and a list (length is 26)
class Solution(object):
def findAnagrams(self, s, p):
n, m, res = len(s), len(p), []
if n < m: return res
p_cnt = [0] * 26
s_cnt = [0] * 26
for i in range(m):
p_cnt[ord(p[i]) - ord('a')] += 1
s_cnt[ord(s[i]) - ord('a')] += 1
if s_cnt == p_cnt:
res.append(0)
for i in range(m, n):
s_cnt[ord(s[i - m]) - ord('a')] -= 1
s_cnt[ord(s[i]) - ord('a')] += 1
if s_cnt == p_cnt:
res.append(i - m + 1)
return res
T: O(n), for loop is O(n) and comparison between two list is O(26) S: O(1)
滑动窗口
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s.length() < p.length()) return res;
int[] map = new int[26];
int cnt = 0;
for (char c : p.toCharArray()) {
if (map[c - 'a'] == 0) cnt++;
map[c - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
map[c - 'a']--;
if (map[c - 'a'] == 0) cnt--;
if (i >= p.length()) {
char d = s.charAt(i - p.length());
if (map[d - 'a'] == 0) cnt++;
map[d - 'a']++;
}
if (cnt == 0) res.add(i + 1 - p.length());
}
return res;
}
}
public List<Integer> findAnagrams(String s, String p) {
int m = s.length(), n = p.length();
if (m < n) {
return new ArrayList();
}
List<Integer> res = new ArrayList<>();
int[] pCount = new int[26];
for (char c : p.toCharArray()) {
pCount[c - 'a']++;
}
int[] sCount = new int[26];
for (int i = 0; i < m; i++) {
char cur = s.charAt(i);
sCount[cur - 'a']++;
if (i >= n) {
sCount[s.charAt(i - n) - 'a']--;
}
if (Arrays.equals(pCount, sCount)) {
res.add(i - n);
}
}
return res;
}
from collections import Counter
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
p_dict = Counter(p)
res = []
td = Counter(s[0:len(p)])
if td == p_dict:
res.append(0)
left = 1
right = len(p)
while right < len(s):
if td[s[left-1]] > 1:
td[s[left-1]] -= 1
else:
del td[s[left-1]]
if s[right] in td:
td[s[right]] += 1
else:
td[s[right]] = 1
if td == p_dict:
res.append(left)
left += 1
right += 1
return res
-Maintain a dictionary of chars in rolling window.
-In each step remove the left most char and add the one next to the right most char.
-Compare the dictionary of rolling window with dictionary of p
Space: O(len(p))
Time: O(N)
# sliding window
# keep a count of valid letters in current sliding window
# every time right index moves, check if count == len(p), and add to res
# 3 cases when right index move
# 1. letter in p and hasn't reached total num of p: increment count, update dict
# 2. letter not in p, move left to right + 1, refresh all (count, dict)
# 3. letter in p but went over boundary: move left until first current letter is removed from the left
# time: O(N), length of s
# space: O(M), number of unique letters in p
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
original_letters = collections.defaultdict(int)
for letter in p:
original_letters[letter] += 1
if len(s) < len(p):
return []
left = 0
# number of valid letters in current sliding window
count = 0
res = []
letters = original_letters.copy()
for right in range(len(s)):
if s[right] in letters and letters[s[right]] > 0:
count += 1
letters[s[right]] -= 1
elif not s[right] in letters :
left = right + 1
count = 0
letters = original_letters.copy()
else:
# too many number of letter s[right] in the window
# remove letter from left
while s[left] != s[right]:
if s[left] in letters:
count -= 1
letters[s[left]] += 1
left += 1
left += 1
if count == len(p):
res.append(left)
return res
from collections import Counter
class Solution:
def findAnagrams(self, s: str, p: str) -> list:
''' method 1 '''
counter = Counter(p)
res = []
for i in range(len(s) - len(p) + 1):
if Counter(s[i:i + len(p)]) == counter:
res.append(i)
return res
''' method 2
much more faster than method 1
time: O(n)
space: O(n)
我们比较两个 hashmap 理论上来说应该是 O(n),但是由于本题的 hashmap 最多只会有 26 个字母,所以是 O(1)
'''
counter = Counter(p)
res = []
tmp = Counter(s[:len(p)])
for i in range(len(p), len(s)+1):
if tmp == counter:
res.append(i-len(p))
if i == len(s):
return res
# modify the tmp
tmp[s[i-len(p)]] -= 1
tmp[s[i]] = tmp.get(s[i], 0) + 1
if tmp[s[i-len(p)]] == 0:
tmp.pop(s[i-len(p)])
return res
s = Solution()
s.findAnagrams('abab', 'ab')
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
p_dict = Counter(p)
res = []
td = Counter(s[0:len(p)])
if td == p_dict:
res.append(0)
left = 1
right = len(p)
while right < len(s):
if td[s[left-1]] > 1:
td[s[left-1]] -= 1
else:
del td[s[left-1]]
if s[right] in td:
td[s[right]] += 1
else:
td[s[right]] = 1
if td == p_dict:
res.append(left)
left += 1
right += 1
return res
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s == null || p == null || s.length() < p.length()) return res;
int[] chars = new int[26];
for (char c : p.toCharArray()) {
chars[c - 'a']++;
}
int start = 0, end = 0;
int count = p.length();
while (end < s.length()) {
if (end - start == p.length() && chars[s.charAt(start++) - 'a']++ >= 0) {
count++;
}
if (--chars[s.charAt(end++) - 'a'] >= 0) {
count--;
}
if (count == 0) res.add(start);
}
return res;
}
}
Using sliding window and hashmap
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
if len(p) > len(s):
return []
result=[]
values=defaultdict(int)
for i in p:
values[i]+=1
left=0
for right in range(len(s)):
if right>=len(p):
values[s[left]]+=1
left+=1
values[s[right]]-=1
if self.is_all_zero(values):
result.append(left)
return result
def is_all_zero(self,values):
for value in values:
if values[value] !=0:
return False
return True
Time: O(n)
Space: O(1)
AC
class Solution {
public List<Integer> findAnagrams(String s, String p) {
HashMap<Character, Integer> mapP = new HashMap<>();
for(char c: p.toCharArray()){
mapP.putIfAbsent(c, 0);
mapP.put(c, mapP.get(c)+1);
}
HashMap<Character, Integer> mapS = new HashMap<>();
List<Integer> res = new ArrayList<>();
for(int i = 0;i < s.length();i++){
char cur = s.charAt(i);
mapS.putIfAbsent(cur, 0);
mapS.put(cur, mapS.get(cur)+1);
if(i >= p.length()) {
int first = i - p.length();
char firstChar = s.charAt(first);
mapS.put(firstChar, mapS.get(firstChar)-1);
if(mapS.get(firstChar) == 0) mapS.remove(firstChar);
}
if(mapS.equals(mapP)) res.add(i - p.length() + 1);
}
return res;
}
}
//优化解 time: O(S+P) space: O(1)
思路: Sliding + hash
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
p_count = collections.Counter(p)
s_count = collections.Counter(s[0: len(p)])
i = lp = len(p)
ls = len(s)
res = []
while i < len(s):
if not p_count - s_count:
res.append(i - lp)
s_count[s[i - lp]] -= 1
s_count[s[i]] += 1
i += 1
if not p_count - s_count:
res.append(i - lp)
return res
晚点回来续写……
使用滑动窗口进行字符串匹配,通过将字符信息存储到哈希表中,如果新加入的字符不满足条件,则不断移动左指针, 直到滑动窗口中的字符满足条件,此时判断满足条件的字符与目标字符个数是否相等
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
char[] chs = s.toCharArray();
char[] chp = p.toCharArray();
int[] target = new int[26];
for (char ch : chp) {
++target[ch - 'a'];
}
int[] cur = new int[26];
int l = 0, r = 0;
while (r < chs.length) {
++cur[chs[r] - 'a'];
while (cur[chs[r] - 'a'] > target[chs[r] - 'a']) {
--cur[chs[l] - 'a'];
++l;
}
if (r - l + 1 == chp.length) {
ans.add(l);
}
++r;
}return ans;
}
}
使用dict记录出现的频率,使用滑动窗口寻找匹配
class Solution:
def findAnagrams(self, s: str, p: str) :
if len(s) < len(p):
return []
result = []
targetDict = collections.defaultdict(int)
for c in p:
targetDict[c] += 1
sDict = collections.defaultdict(int)
slow = 0
for fast in range(len(s)):
if fast < len(p):
if s[fast] in targetDict:
sDict[s[fast]] += 1
if targetDict == sDict:
result.append(slow)
continue
if s[slow] in targetDict:
sDict[s[slow]] -= 1
slow += 1
if s[fast] in targetDict:
sDict[s[fast]] += 1
if targetDict == sDict:
result.append(slow)
return result
时间复杂度 :O(n)
空间复杂度:O(1)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s.length() < p.length()) return res;
int[] map = new int[26];
int cnt = 0;
for (char c : p.toCharArray()) {
if (map[c - 'a'] == 0) cnt++;
map[c - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
map[c - 'a']--;
if (map[c - 'a'] == 0) cnt--;
if (i >= p.length()) {
char d = s.charAt(i - p.length());
if (map[d - 'a'] == 0) cnt++;
map[d - 'a']++;
}
if (cnt == 0) res.add(i + 1 - p.length());
}
return res;
}
}
时间复杂度 :O(n)
空间复杂度:O(1)
class Solution {
public:
vector
if (restCount == 0) /* i - startPos + 1 = len(p) => startPos = i - len(p) + 1 */
res.push_back(i - p.size() + 1);
}
return res;
}
};
Problem Link
Ideas
Complexity: hash table and bucket
Code
from collections import Counter
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
counter_p = Counter(p)
ans = []
for i in range(len(s) - len(p) + 1):
if Counter(s[i:i+len(p)]) == counter_p:
ans.append(i)
return ans
#not some value=0 may lead to mismatch which is wrong,
#set(s) + set(p)
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
counter_p = {key: 0 for key in set(s+p)} #Counter(p)
counter_cur = {key: 0 for key in set(s+p)}#Counter(s[:len(p)])
for character in p:
counter_p[character] += 1
for character in s[:len(p)]:
counter_cur[character] += 1
if counter_cur == counter_p:
ans = [0]
else:
ans = []
for i in range(1, len(s) - len(p) + 1):
#print (i, counter_cur, s[i - 1], s[i + len(p) -1])
counter_cur[s[i - 1]] -= 1
counter_cur[s[i + len(p) -1]] += 1
if counter_cur == counter_p:
ans.append(i)
return ans
滑动窗口。用长度为26的数组记录字符串p中每个字符出现的次数。然后设置宽度为字符串长度p.length()
的滑动窗口,求出字符串s在滑动窗口内的各个字符串出现次数,记录到数组中。如果该数组和字符串p的数组相等,则表示异位词出现。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int slen = s.length();
int plen = p.length();
List<Integer> res = new ArrayList<>();
if (slen < plen) return res;
int[] letters = new int[26];
for (char c : p.toCharArray()) {
letters[c - 'a']++;
}
String pletters = Arrays.toString(letters);
int[] word = new int[26];
for (int i = 0; i < plen; i++) {
word[s.charAt(i) - 'a']++;
}
if (Arrays.equals(letters, word)) res.add(0);
for (int i = 1; i <= slen - plen; i++) {
int pre = s.charAt(i - 1) - 'a';
int post = s.charAt(i + plen - 1) - 'a';
word[pre]--;
word[post]++;
if (Arrays.equals(letters, word)) res.add(i);
}
return res;
}
}
复杂度分析
滑动窗口,用哈希表记录字母
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int len=p.size(),slen=s.size();
vector<int> res;
if(s.size()<len)
return {};
unordered_map<char,int>a,b;
for(char c : p)
b[c]++;
int l=0,r=0;
for(r=0;r<len;r++)
a[s[r]]++;
--r;
while(r<slen)
{
if(a==b)
res.push_back(l);
a[s[l]]--;
if(a[s[l]]==0)
{
a.erase(s[l]);
}
l++;
r++;
a[s[r]]++;
};
return res;
}
};
复杂度分析
思路: 这道题以为之前做过可以立马AC,结果还是不行。 感觉还是需要区分一下什么时候调用计数函数,什么时候直接滑动窗口。 这题可以对不符合要求的直接左移窗口,当长度与目标长度相同时插入数据
func findAnagrams(s string, p string) []int {
out := []int{}
want := [26]int{}
have := [26]int{}
if len(s) < len(p){
return nil
}
for _,x := range p{
want[x-'a']++
}
l:=0
for r:= range s{
have[s[r]-'a']++
for have[s[r]-'a'] > want[s[r]-'a']{
have[s[l]-'a']--
l++
}
if r-l+1 == len(p){
out = append(out,l)
}
}
return out
}
时间复杂度O(n) 空间复杂度O(1)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
int sLen = s.length(), pLen = p.length();
if (pLen > sLen) {
return res;
}
Map<Character, Integer> map = new HashMap<>();
// 记录窗口中能与map匹配的字符
Map<Character, Integer> window = new HashMap<>();
// 初始化p里对应的所有字符
for (int i = 0; i < pLen; i++) {
map.put(p.charAt(i), map.getOrDefault(p.charAt(i), 0) + 1);
}
// valid记录window中有几个字符的数类与map中的匹配
int left = 0, right = 0, valid = 0, count = map.size();
// 滑动窗口开始滑动
while (right < sLen) {
char key = s.charAt(right);
// 如果新字符在窗口内则更新
if (map.containsKey(key)) {
window.put(key, window.getOrDefault(key, 0) + 1);
// 计算在已经valid的情况下,这个key的所属数量又变了也不影响,因为必须所有字符同时达到要求
if (window.get(key).equals(map.get(key))) {
valid++;
}
}
// 窗口更新
if (right - left + 1 == pLen) {
// 符合定义添加
if (valid == count) {
res.add(left);
}
char l = s.charAt(left);
if (map.containsKey(l)) {
if (window.get(l).equals(map.get(l))) {
valid--;
}
window.put(l, window.get(l) - 1);
}
left++;
}
right++;
}
return res;
}
}
思路:
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
target = collections.Counter(p)
k = len(p)
temp = collections.Counter(s[:k])
res = []
if temp== target:
res.append(0)
for i in range(1,len(s)-k+1):
temp[s[i+k-1]] = temp.get(s[i+k-1],0)+1
temp[s[i-1]] -=1
if temp[s[i-1]]==0:
del temp[s[i-1]]
if temp == target:
# print(temp)
res.append(i)
return res
先使用 count 统计 p 中 char 的个数
遍历 string s
每次 将 s[i] 从 count 中消去, 如果消去后 count 为 0, 那么在 count 中删除这个 key
如果 i≥lp 时, 滑动窗口开始
将滑出窗口的 s[i-lp] 在 count 中 +1 (因为之前被消去了)
此时如果 +1 后 count 为 0, 那么在 count 中删除这个 key
当 len(count) == 0 时, 代表所有元素正好都能跟 p 中元素消去, 那么将 i-lp+1 即滑动窗口的左边界加入 res 数组
from collections import defaultdict, Counter
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
count = Counter(p)
ls, lp = len(s), len(p)
res = []
for i in range(ls):
count[s[i]] -= 1
if count[s[i]] == 0:
del count[s[i]]
if i >= lp:
count[s[i-lp]] += 1
if count[s[i-lp]] == 0:
del count[s[i-lp]]
if len(count) == 0:
res.append(i-lp+1)
return res
时间复杂度: O(n) 遍历一次数组的复杂度
空间复杂度: O(1) count 哈希表的复杂度不会超过 O(26) 因为只有 26 个字符, 所以还是常数级别空间
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
target = collections.Counter(p)
ans = []
for i in range(len(s)):
if i >= len(p):
target[s[i - len(p)]] += 1
if target[s[i - len(p)]] == 0:
del target[s[i - len(p)]]
target[s[i]] -= 1
if target[s[i]] == 0:
del target[s[i]]
if len(target) == 0:
ans.append(i - len(p) + 1)
return ans
滑动窗口 \ int[26] 统计p中每个字母出现的次数\ int[26] 滑动统计 s 的substring 中字母的次数\ 比较两个数组
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s == null || p == null || s.length() < p.length()) {
return res;
}
int sLength = s.length();
int pLength = p.length();
int[] target = new int[26];
int[] curSub = new int[26];
for (int i = 0; i < pLength; i++) {
target[p.charAt(i) - 'a']++;
curSub[s.charAt(i) - 'a']++;
}
if (Arrays.equals(target, curSub)) {
res.add(0);
}
for (int i = 1; i <= sLength - pLength; i++) {
curSub[s.charAt(i-1) - 'a']--;
curSub[s.charAt(i+pLength-1) - 'a']++;
if (Arrays.equals(target, curSub)) {
res.add(i);
}
}
return res;
}
}
时间: O(n) \ 空间: O(n)
Sliding window + Hash Table or Array
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
if len(s) < len(p): return []
res = []
memo = [0] * 26
rest = len(p)
for char in p:
memo[ord(char) - ord('a')] += 1
i = 0
for i in range(len(p)):
index = ord(s[i]) - ord('a')
memo[index] -= 1
if memo[index] >= 0:
rest -= 1
if rest == 0: res.append(0)
i = 0
for j in range(len(p), len(s)):
l = ord(s[i]) - ord('a')
r = ord(s[j]) - ord('a')
if memo[l] >= 0:
rest+=1
memo[l] += 1
if memo[r] > 0:
rest-=1
memo[r] -= 1
i+=1
if rest == 0: res.append(i)
return res
Space: O(1) Time: O(len(s))
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res, hash(26, 0), hashZero(26,0);
if(s.length() < p.length()) return res;
for(int i = 0; i < p.length(); i++){
hash[p[i] - 'a']++;
hash[s[i] - 'a']--;
}
if(hash == hashZero) res.push_back(0);
for(int i = p.length(); i < s.length(); i++){
hash[s[i] - 'a']--;
hash[s[i - p.length()] - 'a']++;
if(hash == hashZero) res.push_back(i - p.length() + 1);
}
return res;
}
};
题目并不难, 但是一看上去很唬人. 静下心来先想用什么算法, 分析一下大致时间复杂度, 然后再分析用什么数据结构比较好.
时间复杂度 O(n) 空间是 O(1)
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
res = []
p_dict = {}
for i in p:
if i in p_dict :
p_dict[i] += 1
else:
p_dict[i] = 1
if len(s) < len(p):
return []
s_dict = {}
for i in range(len(s)):
if i < len(p):
if s[i] in s_dict:
s_dict[s[i]] +=1
else:
s_dict[s[i]] =1
else:
s_dict[s[i-len(p)] ] -= 1
if s[i] in s_dict:
s_dict[ s[i]] += 1
else:
s_dict[ s[i]] = 1
flag = True
for key in p_dict:
if key in s_dict:
if p_dict[key] != s_dict[key]:
flag = False
else:
flag = False
if flag:
res.append(i-len(p)+1)
return res
滑动窗口 记录需要的letter和对应的个数,以及sliding window里面的在p中letter的个数
遍历数组,如果遇到p中的letter加入sliding window,用一个valid count来记录是不是遍历了所有的letter
然后以len(p)为窗口更新valid count 和 sliding window
from collections import defaultdict
from typing import List
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
result = []
needed_dict, window_dict = defaultdict(int), defaultdict(int)
for char in p:
needed_dict[char] += 1
left = valid_count = 0
for i, char in enumerate(s):
if char in needed_dict:
window_dict[char] += 1
if window_dict[char] == needed_dict[char]:
valid_count += 1
while i - left + 1 > len(p):
if s[left] in needed_dict:
if window_dict[s[left]] == needed_dict[s[left]]:
valid_count -= 1
window_dict[s[left]] -= 1
left += 1
if valid_count == len(needed_dict):
result.append(left)
return result
time O(n + m) n为s的长度, m为p的长度
Space O(m)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<Integer>();
if (s.length() == 0 || s.length() < p.length()) {return res;}
int[] letterCount = new int[26];
int count = 0;
int left = 0;
int right = 0;
for (Character c : p.toCharArray()) {
letterCount[c - 'a'] += 1;
}
while (right < s.length()) {
while (right < s.length() && right - left + 1 <= p.length()) {
if (letterCount[s.charAt(right) - 'a'] > 0) {count += 1;}
letterCount[s.charAt(right) - 'a'] -= 1;
right++;
}
if (count == p.length()) {
res.add(left);
}
if (letterCount[s.charAt(left) - 'a'] >= 0) {
count -= 1;
}
letterCount[s.charAt(left) - 'a'] += 1;
left++;
}
return res;
}
}
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
if(s.size()<p.size())
return {};
int l = 0, r = -1;
vector<int> freq_s(26, 0), freq_p(26, 0), res;
// 初始化代码值
for( int i = 0 ; i < p.size() ; i++ ){
freq_p[p[i] - 'a' ]++;
freq_s[s[++r] - 'a' ]++;
}
if ( freq_s == freq_p )
res.push_back( l );
// 固定长度的滑动窗口
while( r < s.size()-1 ){
freq_s[s[++r] - 'a' ]++;
freq_s[s[l++] - 'a' ]--;
if ( freq_s == freq_p )
res.push_back( l );
}
return res;
}
};
var findAnagrams = function (s, p) {
const map = p.split('').reduce((map, e) => {
if (map[e]) {
map[e]++
} else {
map[e] = 1
}
return map
}, {})
const res = []
function isInclude(a, map) {
const arrA = a.split('')
for (let i = 0; i < a.length; i++) {
const e = a[i]
if (map[e]) {
map[e]--
} else {
return false
}
}
return true
}
for (let i = 0; i <= s.length - p.length; i++) {
if (map[s[i]] && isInclude(s.slice(i, i + p.length), Object.create(map))) {
res.push(i)
}
}
return res
};
python
class Solution:
def findAnagrams(self, s: str, p: str):
l_s, l_p = len(s), len(p)
if l_s < l_p:
return []
p_set = collections.defaultdict(int)
for e in p:
p_set[e] += 1
res = []
s_set = collections.defaultdict(int)
for l in range(l_s - l_p + 1):
r = l + l_p - 1
if l == 0:
for e in range(l, r + 1):
s_set[s[e]] += 1
else:
s_set[s[r]] += 1
s_set[s[l - 1]] -= 1
if s_set[s[l - 1]] == 0:
s_set.pop(s[l - 1])
if p_set == s_set:
res.append(l)
return res
滑动窗口
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s.length() < p.length()) return res;
int[] key = new int[26];
int[] curKey = new int[26];
for (char ch : p.toCharArray()) key[ch -'a']++;
for (int i = 0; i < p.length(); i++) curKey[s.charAt(i) -'a']++;
if (Arrays.equals(curKey, key)) res.add(0);
for (int i = p.length(); i < s.length(); i++) {
curKey[s.charAt(i - p.length()) - 'a']--;
curKey[s.charAt(i) - 'a']++;
if (Arrays.equals(curKey, key)) res.add(i - p.length() + 1);
}
return res;
}
}
滑动窗口
``
public class Solution extends VersionControl {
public List<Integer> findAnagrams(String s, String p) {
int slen = s.length();
int plen = p.length();
List<Integer> list = new ArrayList<>();
if (plen > slen || plen == 0 || slen == 0) {
return list;
}
//一共只会有26个字母
int[] sCnt = new int[26];
int[] pCnt = new int[26];
//统计p中每个字母出现的次数并存入相应位的数组
for (int i = 0; i < plen; i++) {
sCnt[s.charAt(i) - 'a']++;
pCnt[p.charAt(i) - 'a']++;
}
//如果s中第一个p长度的字符串和p是异位词,存入0
if (Arrays.equals(sCnt, pCnt)) {
list.add(0);
}
//滑动窗口
for (int i = plen; i < slen; i++) {
sCnt[s.charAt(i - plen) - 'a']--;
sCnt[s.charAt(i) - 'a']++;
if (Arrays.equals(sCnt, pCnt)) {
list.add(i - plen + 1);
}
}
return list;
}
}
时间复杂度:O(n)
空间复杂度:O(1)
var findAnagrams = function(s, p) {
if (p.length<1 || s.length<1)
return [];
let map = new Map();
let charNeeded
let count = p.length, right = 0, left = 0;
let output = [];
for (let char of p) {
!map.has(char) ? map.set(char, 1)
: map.set(char, map.get(char)+1);
}
while (right<s.length) {
if(map.get(s[right])>0) {
count--;
}
//console.log("count: ", count, "right:", right)
if(count === 0) output.push(left)
if(map.has(s[right]))
map.set(s[right], map.get(s[right])-1)
//console.log(map,output,s[left], s[left+1], s[right], s[left+2])
right ++
if(right-left >= p.length) {
if(map.get(s[left])>=0) {
count++;
}
map.set(s[left], map.get(s[left])+1)
left ++
}
}
return output;
};
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
counter = collections.Counter(p)
valid_count = 0
left = 0
result = []
for right in range(len(s)):
if s[right] in counter:
if counter[s[right]] > 0:
valirightd_count += 1
counter[s[]] -= 1
while valid_count == len(p):
if right - left + 1 == len(p):
result.append(left)
if s[left] in counter:
counter[s[left]] += 1
if counter[s[left]] > 0:
valid_count -= 1
left += 1
return result
https://leetcode.com/problems/find-all-anagrams-in-a-string/
hashtable + slide window
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
if len(s) < len(p):
return []
d_p = collections.Counter(p)
d_s = defaultdict(int)
result = []
for i in range(len(s)):
if i < len(p):
d_s[s[i]] += 1
else:
d_s[s[i-len(p)]] -= 1
d_s[s[i]] += 1
is_anagram = True
for e, cnt in d_p.items():
if e not in d_s or cnt != d_s[e]:
is_anagram = False
break
if is_anagram:
result.append(i-len(p) + 1)
return result
时间复杂度: O(Ns + Np) 空间复杂度:O(1)
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
# create a window
win = [0]*26
for c in p:
win[ord(c)-ord('a')] += 1
res = []
for i in range(len(s)):
# initialize win
if i<len(p)-1:
if s[i] in p:
win[ord(s[i])-ord('a')] -= 1
else:
if s[i] in p:
win[ord(s[i])-ord('a')] -= 1
if sum(win) == 0 and min(win)>=0:
res.append(i-len(p)+1)
if s[i-len(p)+1] in p:
win[ord(s[i-len(p)+1])-ord('a')] += 1
return res
438. 找到字符串中所有字母异位词
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/
前置知识
题目描述
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
字母异位词指字母相同,但排列不同的字符串。 不考虑答案输出的顺序。 示例 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" 的字母异位词。