Open azl397985856 opened 2 years ago
Typical sliding window problem\ needs 1. a window dictionary to record when moving right pointer and left pointer\ also 2. a match count to record if the required subarray has been found\ pay attention to where to update the result.
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
pDict = collections.defaultdict(int)
for char in p:
pDict[char] += 1
left = right = match = 0
window = collections.defaultdict(int)
res = []
while right < len(s):
c = s[right]
right += 1
if c in pDict:
window[c] += 1
if window[c] == pDict[c]:
match += 1
while right - left >= len(p):
if match == len(pDict):
res.append(left)
d = s[left]
if d in pDict:
if window[d] == pDict[d]:
match -= 1
window[d] -= 1
left += 1
return res
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
unordered_map<char, int> cnt;
for (auto c: p) cnt[c] ++ ;
vector<int> res;
int tot = cnt.size();
for (int i = 0, j = 0, satisfy = 0; i < s.size(); i ++ ) {
if ( -- cnt[s[i]] == 0) satisfy ++ ;
while (i - j + 1 > p.size()) {
if (cnt[s[j]] == 0) satisfy -- ;
cnt[s[j ++ ]] ++ ;
}
if (satisfy == tot) res.push_back(j);
}
return res;
}
};
滑动窗口 + hashmap
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
# 写的太丑了
# 需要两个map, 一个用来存放p, 另一个作为sliding window
# 两个pointer,left和right, left 用来检测被扔出去的元素
mapp = {}
for chara in p:
mapp[chara] = mapp.get(chara, 0) + 1
maps = {}
for elem in s[: len(p)]:
maps[elem] = maps.get(elem, 0) + 1
# 检查maps和mapp是否完全一致,如果一致就说明这个子字符串是anagram
if len(s) < len(p):
return []
left, right = 0, len(p) - 1
output = []
if maps == mapp:
output.append(left)
while right < len(s) - 1:
maps[s[left]] -= 1
if maps[s[left]] == 0:
del maps[s[left]]
left += 1
right += 1
maps[s[right]] = maps.get(s[right], 0) + 1
if maps == mapp:
output.append(left)
return output
时间复杂度:O(n) 空间复杂度:O(n)
Explanation
Python
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
d = [0 for _ in range(26)]
for ch in p:
index = ord(ch) - ord("a")
d[index] += 1
t = [0 for _ in range(26)]
output = []
n = len(p)
for i in range(len(s)):
t[ord(s[i]) - ord('a')] += 1
if i >= n:
t[ord(s[i - n]) - ord('a')] -= 1
if t == d:
output.append(i - n + 1)
return output
Complexity:
O(N)
O(1)
C++ Code:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> pRecord(26,0);
vector<int> sRecord(26,0);
for(int i=0; i<p.size(); i++)
pRecord[p[i]-'a']++;
int left =0;
int right =0;
int count =0;
vector<int> ret;
while(right < s.size())
{
int index = s[right]-'a';
sRecord[index]++;
if(pRecord[index]>0 && sRecord[index]<=pRecord[index])
{
count++;
}
while(count == p.size())
{
if(right - (left) +1 == p.size())
{
ret.push_back(left);
}
int leftindex = s[left]-'a';
sRecord[leftindex]--;
if(pRecord[leftindex]>0 && sRecord[leftindex]<pRecord[leftindex])
{
count--;
}
left++;
}
/// [left right] include all p letter.
right ++ ;
}
return ret;
}
};
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> ret;
if(s.size()<p.size())
return ret;
vector<int> pRecord(26,0);
vector<int> sRecord(26,0);
for(int i=0; i<p.size(); i++)
pRecord[p[i]-'a']++;
int count =0;
for(int i=0; i<p.size(); i++)
{
int index = s[i] - 'a';
sRecord[index]++;
if(pRecord[index]>0 && sRecord[index] <=pRecord[index])
count++;
}
if(count == p.size())
ret.push_back(0);
int left =1;
int right = p.size();
while(right<s.size())
{
int rightIndex = s[right] - 'a';
int leftIndex = s[left-1] - 'a';
if(rightIndex !=leftIndex)
{
sRecord[rightIndex]++;
if(pRecord[rightIndex]>0 && sRecord[rightIndex] <=pRecord[rightIndex])
count++;
sRecord[leftIndex]--;
if(pRecord[leftIndex]>0 && sRecord[leftIndex] <pRecord[leftIndex])
count--;
}
if(count == p.size())
ret.push_back(left);
left++;
right++;
}
return ret;
}
};
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> list = new ArrayList<>();
if (s == null || s.length() == 0 || p == null || p.length() == 0) return list;
int[] hash = new int[256];
// 记录p中每个char的出现次数
for (char c : p.toCharArray()) {
hash[c]++;
}
// left 和 right 组成窗口
int left = 0, right = 0, count = p.length();
while (right < s.length()) {
// >= 1 说明p中有这个字符
if (hash[s.charAt(right++)]-- >= 1) count--;
if (count == 0) list.add(left);
// >= 0 说明p中有这个字符
if (right - left == p.length() && hash[s.charAt(left++)]++ >= 0) count++;
}
return list;
}
}
Go Code:
func findAnagrams(s string, p string) []int {
var win, need [26]int
for _, v := range p {
need[v-97]++
}
left, valid := 0, 0
var res []int
for right, c := range s {
c -= 97
if need[c] != 0 {
win[c]++
if win[c] == need[c] {
valid+=win[c]
}
}
for right-left+1 == len(p) {
if valid == need[0]+need[1]+need[2]+need[3]+need[4]+need[5]+need[6]+need[7]+need[8]+need[9]+need[10]+need[11]+need[12]+need[13]+need[14]+need[15]+need[16]+need[17]+need[18]+need[19]+need[20]+need[21]+need[22]+need[23]+need[24]+need[25] {
res = append(res, left)
}
d := s[left] - 97
left++
if need[d] != 0 {
if win[d] == need[d] {
valid-=win[d]
}
win[d]--
}
}
}
return res
}
复杂度分析
令 n 为数组长度。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int pLength = p.length();
int sLength = s.length();
char[] sArray = s.toCharArray();
int[] letterCnt = new int[26];
int[] window = new int[26];
for(int letter : p.toCharArray()) {
letterCnt[letter - 'a'] ++;
}
List<Integer> res = new ArrayList<>();
for(int i = 0; i < s.length(); i++) {
window[sArray[i] - 'a'] ++;
if(i >= pLength) {
window[sArray[i - pLength] - 'a'] --;
}
if(i >= pLength - 1 && Arrays.equals(letterCnt, window)) {
res.add(i - pLength + 1);
}
}
return res;
}
}
思路:使用滑动窗口,构建滑动窗口有二种方式,本文采用的是使用Hash来构建,(但是从结果运行效果来看,使用数组的效率要高于使用Hash)
class Solution { public List<Integer> findAnagrams(String s, String p) { //使用滑动窗口 HashMap<Character,Integer> set = new HashMap<>(); for(int i =0;i<p.length();i++){ set.put(p.charAt(i),set.getOrDefault(p.charAt(i),0)+1); } HashMap<Character,Integer> set1 = new HashMap<>(set); List<Integer> list = new ArrayList<>(); int left =0; for(int j = 0;j<s.length();j++){ char t = s.charAt(j); if(set1.containsKey(t)){ int temp =set1.get(t); if(temp==1){ set1.remove(t); }else{ set1.put(t,temp-1); } }else if(!set.containsKey(t)){ set1 = new HashMap<>(set); left = j+1; }else if(set.containsKey(t)){ //处理滑动窗口的左边 while (left<j&& !set1.containsKey(t)){ set1.put(s.charAt(left),set1.getOrDefault(s.charAt(left),0)+1); left++; } //处理滑动窗口的右边 int temp = set1.get(t); if(temp==1){ set1.remove(t); }else{ set1.put(t,temp-1); } } if(set1.size()==0){ list.add(left); } } System.out.println(list.toString()); return list; } }
https://leetcode.com/problems/find-all-anagrams-in-a-string/
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if(p.length() > s.length()){
return new ArrayList<>();
}
List<Integer> result = new ArrayList<>();
int[] base = new int[26];
for(char c: p.toCharArray()){
base[c - 'a']++;
}
int[] curr = new int[26];
for(int i = 0; i < p.length(); i++){
curr[s.charAt(i) - 'a']++;
}
if(isValid(base, curr)){
result.add(0);
}
for(int i = p.length(); i < s.length(); i++){
curr[s.charAt(i) - 'a']++;
curr[s.charAt(i - p.length()) - 'a']--;
if(isValid(base, curr)){
result.add(i - p.length() + 1);
}
}
return result;
}
private boolean isValid(int[] base, int[] curr){
if(base.length != curr.length){
return false;
}
for(int i = 0; i < base.length; i++){
if(base[i] != curr[i]){
return false;
}
}
return true;
}
}
https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/submissions/
滑动窗口,使用数组记录频率
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int n = p.length();
List<Integer> list = new LinkedList<>();
int[] freq = new int[256];
int count = 0;
for(int i = 0; i < n; i++) {
char ch = p.charAt(i);
freq[ch]++;
}
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
freq[ch]--;
if(freq[ch] >= 0) {
count++;
}
if(i - n >= 0) {
freq[s.charAt(i - n)]++;
if(freq[s.charAt(i - n)] > 0) {
count--;
}
}
if(count == n) {
list.add(i - n + 1);
}
}
return list;
}
}
O(n)
O(1)
滑动窗口长度为p.length()。首先统计s和p的前p.length()个字母构造第一个窗口,之后向右滑动更新窗口,若统计s和p的数组相同则记录到结果中。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s.length() < p.length()){
return res;
}
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < p.length(); i++){
sCount[s.charAt(i) - 'a']++;
pCount[p.charAt(i) - 'a']++;
}
if (Arrays.equals(sCount, pCount)){
res.add(0);
}
for (int i = p.length(); i < s.length(); i++){
sCount[s.charAt(i - p.length()) - 'a']--;
sCount[s.charAt(i) - 'a']++;
if (Arrays.equals(sCount,pCount)){
res.add(i - p.length() + 1);
}
}
return res;
}
}
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
freq用来计算字母出现的次数,在滑动窗口内则减去,否则就加上,用另一个空vector来判断是否是anagram。先把p全部放入freq,并减去从0开始把p长度的s substring的出现次数。如果此时freq为空,则把index 0 放入结果中。然后从p.size开始遍历,在固定大小的窗口中,i-len(p)是窗口外的最后一个字符,把他加回到freq中进行计算,同时把当前的字符s[i]从freq中减去,同样,如果freq为空,证明这个窗口也符合题意,需要加入结果vector中。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res, freq(26, 0), check(26, 0);
if (s.size() < p.size()) return res;
for (int i = 0; i < p.size(); ++i) {
freq[p[i] - 'a']++; // count chars in p
freq[s[i] - 'a']--;
}
if (freq == check) res.push_back(0);
for (int i = p.size(); i < s.size(); ++i) {
freq[s[i - p.size()] - 'a']++;
freq[s[i] - 'a']--;
if (freq == check) res.push_back(i - p.size() + 1);
}
return res;
}
};
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new LinkedList<>();
if (s == null || p == null || s.length() < p.length())
return res;
int[] ch = new int[26];
//统计p串字符个数
for (char c : p.toCharArray())
ch[c - 'a']++;
//把窗口扩成p串的长度
int start = 0, end = 0, rest = p.length();
for (; end < p.length(); end++) {
char temp = s.charAt(end);
ch[temp - 'a']--;
if (ch[temp - 'a'] >= 0)
rest--;
}
if (rest == 0)
res.add(0);
//开始一步一步向右移动窗口。
while (end < s.length()) {
//左边的拿出来一个并更新状态
char temp = s.charAt(start);
if (ch[temp - 'a'] >= 0)
rest++;
ch[temp - 'a']++;
start++;
//右边的拿进来一个并更新状态
temp = s.charAt(end);
ch[temp - 'a']--;
if (ch[temp - 'a'] >= 0)
rest--;
end++;
// 状态合法就存到结果集合
if (rest == 0)
res.add(start);
}
return res;
}
sliding window using counderMap
Java Code:
class Solution {
public List<Integer> findAnagrams(String s, String p) {
// p can have repeated letters
// fixed size sliding window
int matchNum = 0;
Map<Character, Integer> map = new HashMap<>();
Map<Character, Integer> compareMap = new HashMap<>();
List<Integer> result = new ArrayList<>();
int n = s.length();
int m = p.length();
if (n < m) {
return result;
}
char[] sArr = s.toCharArray();
char[] pArr = p.toCharArray();
// need to compare against the values in compareMap
for (int i = 0; i < m; i++) {
compareMap.put(pArr[i], compareMap.getOrDefault(pArr[i], 0) + 1);
}
// filling with the initial m values of sliding window
for (int i = 0; i < m; i++) {
if (compareMap.containsKey(sArr[i])) {
int tempCount = map.getOrDefault(sArr[i], 0);
if (tempCount + 1 == compareMap.get(sArr[i])) {
matchNum++;
}
map.put(sArr[i], tempCount + 1);
}
}
if (matchNum == compareMap.size()) {
result.add(0);
}
for (int i = 1; i <= n - m; i++) {
// after filling the first m letters;
// sArr[i - 1] tp be removed from map
if (compareMap.containsKey(sArr[i - 1])) {
int tempCount = map.getOrDefault(sArr[i - 1], 0);
if (tempCount == compareMap.get(sArr[i - 1])) {
matchNum--;
}
map.put(sArr[i - 1], tempCount - 1);
}
if (compareMap.containsKey(sArr[i + m - 1])) {
int tempCount = map.getOrDefault(sArr[i + m - 1], 0);
if (tempCount + 1 == compareMap.get(sArr[i + m - 1])) {
matchNum++;
}
map.put(sArr[i + m - 1], tempCount + 1);
}
if (matchNum == compareMap.size()) {
result.add(i);
}
}
return result;
}
}
复杂度分析
Python 3 code
from collections import Counter
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
res = []
pCounter = Counter(p)
sCounter = Counter(s[:len(p)-1])
for i in range(len(p)-1,len(s)):
sCounter[s[i]] += 1
if sCounter == pCounter:
res.append(i-len(p)+1)
sCounter[s[i-len(p)+1]] -= 1
if sCounter[s[i-len(p)+1]] == 0:
del sCounter[s[i-len(p)+1]]
return res
···
TC:O(N)
SC:O(1)
var findAnagrams = function(s, p) {
if (p.length > s.length) return [];
var l = 0, r = 0, res = [], map = {}, win = {}, match = 0;
for (var i = 0; i < p.length; i++) {
map[p[i]] = (map[p[i]] || 0) + 1;
}
while (r < s.length) {
var c = s[r];
if (map[c]) {
win[c] = (win[c] || 0) + 1;
if (win[c] === map[c]) {
match++;
}
}
r++;
if (r - l >= p.length) {
if (match === Object.keys(map).length) {
res.push(l);
}
var lStr = s[l];
if (map[lStr] && win[lStr]) {
if (map[lStr] === win[lStr]) {
match--;
}
win[lStr]--;
}
l++;
}
}
return res;
};
时间:O(n) 空间:O(1)
思路: record all characters in p with map (since the characters are lowercase letters only, so use int array for simplicity), then maintain a sliding window for s, for each new character of the window, minus one for the corresponding key in the map, for each past character of the window, plus one for the corresponding key in the map, for each valid sliding window, check the map, if all keys have zero value, then it means all characters in the sliding window matches all characters in p
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>();
// corner cases
if (s == null || p == null || s.length() < p.length() || s.length() == 0 || p.length() == 0) {
return result;
}
// length of sliding window
int pLength = p.length();
int[] map = new int[26];
for (int i = 0; i < pLength; i++) {
Character ch = p.charAt(i);
map[ch - 'a']++;
}
int sIdx = 0;
while (sIdx < s.length()) {
Character ch = s.charAt(sIdx);
if (sIdx < pLength) {
map[ch - 'a']--;
} else {
map[ch - 'a']--;
map[s.charAt(sIdx - pLength) - 'a']++;
}
if (isAnagram(map)) {
result.add(sIdx - pLength + 1);
}
sIdx++;
}
return result;
}
private boolean isAnagram(int[] map) {
for (int i = 0; i < map.length; i++) {
if (map[i] != 0) {
return false;
}
}
return true;
}
}
Time complexity: O(n), n is the length of s Space complexity: O(1)
每次找到一个anagram,则append到res,变化size的滑动窗口。
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
counter = dict(collections.Counter(p))
curr = collections.defaultdict(int)
for i in s[:len(p)]:
curr[i]+=1
ans = []
if curr == counter:
ans.append(0)
for i in range(len(p),len(s)):
curr[s[i]]+=1
if curr[s[i-len(p)]] == 1:
curr.pop(s[i-len(p)])
else:
curr[s[i-len(p)]]-=1
if curr == counter:
ans.append(i-len(p)+1)
return ans
复杂度分析
用一个定长的窗口扫一遍s,把s和p重合的词放进hash,窗口扫的hash和目标hash进行对比,一样则加到结果中
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
if len(p) > len(s):
return []
target_hash_map = dict()
for w in p:
target_hash_map[w] = target_hash_map.get(w, 0) + 1
hash_map = dict()
l = 0
res = []
for r, w in enumerate(s):
if w in target_hash_map:
hash_map[w] = hash_map.get(w, 0) + 1
if r >= len(p):
if s[l] in target_hash_map:
hash_map[s[l]] = hash_map.get(s[l]) - 1
l += 1
if hash_map == target_hash_map:
res.append(l)
return res
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
int sLen = s.length();
int pLen = p.length();
int[] sStr = new int[26];
int[] pStr = new int[26];
if (pLen > sLen) return ans;
for (int i = 0; i < pLen; i ++) {
sStr[s.charAt(i) - 'a'] ++;
pStr[p.charAt(i) - 'a'] ++;
}
if (Arrays.equals(sStr, pStr)) ans.add(0);
for (int j = pLen; j < sLen; j ++) {
sStr[s.charAt(j) - 'a'] ++;
sStr[s.charAt(j - pLen) - 'a'] --;
if (Arrays.equals(sStr, pStr)) ans.add(j - pLen + 1);
}
return ans;
}
}
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int n=s.size(),m=p.size();
if(m>n)return {};
vector<int> ans,cnt_s(26),cnt_p(26);
int i;
for(i=0;i<m;i++){
cnt_p[p[i]-'a']++;
cnt_s[s[i]-'a']++;
}
if(cnt_p==cnt_s)ans.push_back(0);
for(;i<n;i++){
cnt_s[s[i]-'a']++;
cnt_s[s[i-m]-'a']--;
if(cnt_p==cnt_s)ans.push_back(i-m+1);
}
return ans;
}
};
class Solution {
public:
vector
public class Solution {
public List<Integer> findAnagrams(String s, String p) {
ArrayList<Integer> soln = new ArrayList<Integer>();
if (s.length() == 0 || p.length() == 0 || s.length() < p.length()){
return new ArrayList<Integer>();
}
int[] chars = new int[26];
for (Character c : p.toCharArray()){
chars[c-'a']++;
}
int start = 0, end = 0, len = p.length(), diff = len;
char temp;
for (end = 0; end < len; end++){
temp = s.charAt(end);
chars[temp-'a']--;
if (chars[temp-'a'] >= 0){
diff--;
}
}
if (diff == 0){
soln.add(0);
}
while (end < s.length()){
temp = s.charAt(start);
if (chars[temp-'a'] >= 0){
diff++;
}
chars[temp-'a']++;
start++;
temp = s.charAt(end);
chars[temp-'a']--;
if (chars[temp-'a'] >= 0){
diff--;
}
if (diff == 0){
soln.add(start);
}
end++;
}
return soln;
}
}
参考官方解法
语言:Java
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if(s==null||p==null||s.length()<p.length()) {
return new ArrayList<Integer>();
}
List<Integer> list = new ArrayList<Integer>();
int[] ch = new int[26];
for(int i=0;i<p.length();i++) {
ch[p.charAt(i)-'a']++;
}
int rest = p.length();
for(int i=0;i<p.length();i++) {
char temp = s.charAt(i);
ch[temp-'a']--;
if(ch[temp-'a']>=0) {
rest--;
}
}
if(rest==0) {
list.add(0);
}
int start=0,end=p.length();
while(end<s.length()) {
char temp = s.charAt(start);
if(ch[temp-'a']>=0) {
rest++;
}
ch[temp-'a']++;
start++;
temp = s.charAt(end);
ch[temp-'a']--;
if(ch[temp-'a']>=0) {
rest--;
}
end++;
if(rest==0) {
list.add(start);
}
}
return list;
}
}
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
const findAnagrams = function(s, p) {
const res = [];
const offset = 'a'.charCodeAt(0);
const counter = Array(26).fill(0);
for (let i = 0; i < p.length; i++) {
counter[p.charCodeAt(i) - offset] += 1;
}
let start = 0;
for (let i = 0; i < s.length; i++) {
counter[s.charCodeAt(i) - offset] -= 1;
while (start <= i && counter[s.charCodeAt(i) - offset] < 0) {
counter[s.charCodeAt(start++) - offset] += 1;
}
if (i - start + 1 === p.length) res.push(start);
}
return res;
};
class Solution {
public:
vector<int> findAnagrams(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
vector<int> res; // 记录结果
while (right < s.size()) {
char c = s[right];
right++;
// 进行窗口内数据的一系列更新
if (need.count(c)) {
window[c]++;
if (window[c] == need[c])
valid++;
}
// 判断左侧窗口是否要收缩
while (right - left >= t.size()) {
// 当窗口符合条件时,把起始索引加入 res
if (valid == need.size())
res.push_back(left);
char d = s[left];
left++;
// 进行窗口内数据的一系列更新
if (need.count(d)) {
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
return res;
}
};
var findAnagrams = function(s, p) {
let res = []
let left = 0, right = 0
const map = new Map()
let rest = p.length
for (let i = 0; i < p.length; i++) {
map.set(p[i], map.get(p[i]) + 1 || 1)
}
for (; right < p.length; right++) {
map.set(s[right], map.get(s[right]) === undefined ? -1 : map.get(s[right]) - 1)
if (map.get(s[right]) >= 0) {
rest--
}
}
if (rest === 0) {
res.push(left)
}
while (right <= s.length) {
if (map.get(s[left]) >= 0) {
rest++
}
map.set(s[left], map.get(s[left]) === undefined ? 1 : map.get(s[left]) + 1)
left++
map.set(s[right], map.get(s[right]) === undefined ? -1 : map.get(s[right]) - 1)
if (map.get(s[right]) >= 0) {
rest--
}
right++
if (rest === 0) {
res.push(left)
}
}
return res
};
时间:O(n) 空间:O(1)
思路:滑动窗口
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if(s == null || s.length() < p.length()){
return new ArrayList<>();
}
// 存储结果的结果集
List<Integer> res = new ArrayList<>();
// 先对目标串p每个字符进行字符计数,统计出每个字符的出现次数
int[] pCnt = new int[26];
for(int i = 0; i < p.length(); i++){
pCnt[p.charAt(i) - 'a']++;
}
//start和end分别控制窗口的开端和末端
int[] window = new int[26];
int start = 0, end = 0;
while(end < s.length()){
window[s.charAt(end) - 'a']++;
//维护一个长度固定的滑动窗口
if((end - start + 1) == p.length()){
//如果出现了符合条件的
if(Arrays.equals(pCnt, window)){
res.add(start);
}
//把左端的频率清除,左端向前加一
window[s.charAt(start) - 'a']--;
start++;
}
end++;
}
return res;
}
}
时间复杂度:O(N) 空间复杂度:O(1)
【day45】438. 找到字符串中所有字母异位词
https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/
思路:p大小的固定滑动窗口,哈希表记录p,看窗口里的是否一致就可,字母是否出现和出现的次数,如果窗口内满足,左指针下标添加到结果
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
target = collections.Counter(p)
ans = []
window = len(p)
for i in range(len(s)):
if i >=len(p):
target[s[i-window]] +=1 #字符在里面 a[bcd] 对窗口内的bcd计数
if target[s[i-window]] == 0:
del target[s[i-window]] # 哈希表的 key 是字符,value 是窗口内字符出现次数。这样当 value 为 0 时,我们移除 key,这样当哈希表容量为 0,说明我们找到了一个异位词。
target[s[i]] -= 1
if target[s[i]] == 0:
del target[s[i]]
if len(target) == 0:
ans.append(i-window+1)
return ans
复杂度分析:
设置表示p中每个字母个数的数组parray,设置count表示p还剩几个字符未与s子字符串匹配。count为0时,匹配成功,将左边界加入结果数组。 s窗口右边界上的元素若出现在p中,则count--,表示剩余未匹配的字符数量减少一个。
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
var findAnagrams = function(s, p) {
let count = p.length,
result = [],
left = 0,
right = 0,
parray = Array(27).join('0').split('').map(x => parseInt(x)); // 26个0组成的数组
for (let pc of p) {
parray[pc.charCodeAt() - 97] ++; // parray初始化
}
while (right < s.length) {
if (parray[s[right++].charCodeAt() - 97]-- >= 1) count--;
if (count === 0) result.push(left);
if (right - left === p.length && parray[s[left++].charCodeAt() - 97] ++ >= 0) count ++;
}
return result;
};
from collections import Counter
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
res = []
M, N = len(s), len(p)
p_cnt = Counter(p)
s_cnt = Counter(s[:N-1])
for i in range(N - 1, M):
s_cnt[s[i]] += 1
if s_cnt == p_cnt:
res.append(i - N + 1)
s_cnt[s[i - N + 1]] -= 1
if s_cnt[s[i - N + 1]] == 0:
del s_cnt[s[i - N + 1]]
return res
var findAnagrams = function(s, p) {
let pMap = new Map(),sMap = new Map();
let left=0,right=0,res=[],count=0;
for(let i = 0;i < p.length;i++){
let char = p[i];
if(pMap.has(char)){
pMap.set(char,pMap.get(char)+1);
}else{
pMap.set(char,1);
}
}
while(right<s.length){
let char = s[right];
if(pMap.has(char)){
if(!sMap.has(char)){
sMap.set(char,1);
}else{
sMap.set(char,sMap.get(char)+1);
}
if(sMap.get(char)==pMap.get(char)){
count++;
}
}
right++;
while(count==pMap.size){
let tempChar = s[left];
if(pMap.has(tempChar)){
sMap.set(tempChar,sMap.get(tempChar)-1);
if(sMap.get(tempChar)<pMap.get(tempChar)){
count--;
}
}
if(right-left == p.length){
res.push(left);
}
left++;
}
}
return res;
};
var findAnagrams = function (s, p) {
let slow = 0, fast = 0, res = [];
let needArray = new Array(26).fill(0);
for (let i = 0; i < p.length; i++) {
needArray[p[i].charCodeAt() - "a".charCodeAt()]++;
}
let hasArray = new Array(26).fill(0);
while (fast < s.length) {
if (fast - slow + 1 < p.length) {
hasArray[s[fast].charCodeAt() - "a".charCodeAt()]++;
fast++;
} else {
hasArray[s[fast].charCodeAt() - "a".charCodeAt()]++;
if (hasArray.toString() === needArray.toString()) {
res.push(slow);
}
fast++;
hasArray[s[slow].charCodeAt() - "a".charCodeAt()]--;
slow++;
}
}
return res;
};
class Solution {
public List
int[] pCnt = new int[26];
int[] sCnt = new int[26];
for(int i = 0; i < m; i++){
pCnt[p.charAt(i) - 'a'] ++;
}
int left = 0;
for(int right = 0; right < n; right++){
int curRight = s.charAt(right) - 'a';
sCnt[curRight]++;
while(sCnt[curRight] > pCnt[curRight]){
int curLeft = s.charAt(left) - 'a';
sCnt[curLeft]--;
left++;
}
if(right - left + 1 == m){
res.add(left);
}
}
return res;
}
}
滑动窗口原理 窗口内的字符由一个分开的列表管理
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
import collections
res = []
len_s,len_p = len(s), len(p)
list_p = [0 for _ in range (26)]
list_s = [0 for _ in range (26)]
for i in p:
list_p[ord(i)-ord('a')]+=1
for i in range(len_s):
if i>=len_p:
list_s[ord(s[i-len_p]) - ord('a')]-=1
list_s[ord(s[i])-ord('a')]+=1
if list_p == list_s:
res.append(i-len_p+1)
return res
时间复杂度: O(N) 空间复杂度: O(N)
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
# o n
滑动窗口 哈希
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
Map<Character, Integer> need = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for (int i = 0; i < p.length(); i++) {
need.put(p.charAt(i), need.getOrDefault(p.charAt(i), 0) + 1);
}
int left = 0, right = 0, valid = 0;
while (right < s.length()){
char c = s.charAt(right);
right++;
if (need.get(c) != null){
window.put(c, window.getOrDefault(c, 0) + 1);
if (window.get(c).intValue() == need.get(c).intValue()){
valid += need.get(c);
}
}
while (valid == p.length()){
if ((right - left) == p.length()){
res.add(left);
}
char d = s.charAt(left);
left++;
if (need.get(d) != null){
if (window.get(d).intValue() == need.get(d).intValue()){
valid -= need.get(d);
}
window.put(d, window.getOrDefault(d, 0) - 1);
}
}
}
return res;
}
滑动窗口+列表
class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: temp=[0]*26 if len(s)<len(p): return []
for i in range(len(p)):
idx=ord(p[i])-ord("a")
temp[idx]+=1
res=[]
left=0
right=0
for right in range(len(p)):
idx=ord(s[right])-ord("a")
temp[idx]-=1
if max(temp)==0 and min(temp)==0:
res.append(0)
while right<len(s)-1:
right+=1
idx1=ord(s[right])-ord("a")
temp[idx1]-=1
idx2=ord(s[left])-ord("a")
temp[idx2]+=1
left+=1
if max(temp)==0 and min(temp)==0:
res.append(left)
return res
时间复杂度 : O(n)
空间复杂度:O(n)
滑动窗口
public static List<Integer> findAnagrams(String s, String p) {
//两字符串的长度
int n = s.length(), m = p.length();
List<Integer> res = new ArrayList<>();
// 如果s的长度小于p,则表明不存在异位词
if (n < m) {
return res;
}
int[] pCnt = new int[26];
int[] sCnt = new int[26];
for (int i = 0; i < m; i++) {
pCnt[p.charAt(i) - 'a']++;
}
int left = 0;
for (int right = 0; right < n;right++) {
int curRight = s.charAt(right) - 'a';
sCnt[curRight]++;
while (sCnt[curRight] > pCnt[curRight]) {
int curLeft = s.charAt(left) - 'a';
sCnt[curLeft]--;
left++;
}
if (right - left + 1 == m) {
res.add(left);
}
}
return res;
}
var findAnagrams = function (s, p) {
let cnt_s = new Array(26).fill(0), cnt_p = new Array(26).fill(0), res = [];
for (let item of p) {
let index = item.charCodeAt() - 'a'.charCodeAt();
cnt_p[index]++;
}
for (let item of s.slice(0, p.length)) {
let index = item.charCodeAt() - 'a'.charCodeAt();
cnt_s[index]++;
}
if (cnt_s.toString() === cnt_p.toString()) res.push(0)
for (let i = p.length; i < s.length; i++) {
let pre = s[i - p.length].charCodeAt() - 'a'.charCodeAt();
let next = s[i].charCodeAt() - 'a'.charCodeAt()
cnt_s[pre]--;
cnt_s[next]++;
if (cnt_s.toString() === cnt_p.toString()) res.push(i - p.length +1)
}
return res
};
时间复杂度:O(n) 空间复杂度:O(1)
滑动窗口+数组记录
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
p_count = [0] * 26
s_count = [0] * 26
res = []
for a in p:
p_count[ord(a) - ord('a')] += 1
left = 0
for right in range(len(s)):
if right < len(p) - 1:
s_count[ord(s[right]) - ord('a')] += 1
continue
s_count[ord(s[right]) - ord('a')] += 1
if p_count == s_count:
res.append(left)
s_count[ord(s[left]) - ord('a')] -= 1
left += 1
return res
时间:O(N)
空间:O(1)
哈希表 + 滑动窗口
var findAnagrams = function (s, p) {
let res = []
if (s.length < p.length) return res
let needsMap = new Map()
for (let str of p) {
needsMap.set(str, needsMap.has(str) ? needsMap.get(str) + 1 : 1)
}
let count = 0
let left = right = 0
let windowsMap = new Map()
while (right < s.length) {
let str = s[right]
if (needsMap.has(str)) {
windowsMap.set(str, windowsMap.has(str) ? windowsMap.get(str) + 1 : 1)
if (windowsMap.get(str) == needsMap.get(str)) {
count++
}
}
console.log(count);
right++
if (right - left >= p.length) {
// 说明批评到对应数量
if (count == needsMap.size) {
res.push(left)
}
let first = s[left]
if (needsMap.has(first)) {
if (windowsMap.has(first)) {
// 说明最左边在滑动的窗口中、得去掉
if (needsMap.get(first) == windowsMap.get(first)) {
count--
}
windowsMap.set(first, windowsMap.get(first) - 1)
}
}
left++
}
}
return res
};
滑动窗口
代码
class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: wins = dict() valid = 0 left = 0 right = 0 ans = [] sn = len(s) pn = len(p) for i in p: if i in wins: wins[i] += 1 else: wins[i] = 1 wn = len(wins) while right < sn: s_i = s[right] if s_i in wins: wins[s_i] -= 1 if wins[s_i] == 0: valid += 1 if valid == wn: ans.append(left) if right >= pn - 1: s_r_i = s[left] if s_r_i in wins: if wins[s_r_i] == 0: valid -= 1 wins[s_r_i] += 1 left += 1 right += 1 return ans
复杂度
时间:O(N)
空间:O(N)
思路
滑动窗口+hash表
代码
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
HashMap<Character,Integer> map = new HashMap<>();
char[] chars = p.toCharArray();
for (char c : chars) {
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;
}
}
时间复杂度
时间复杂度:O(N)
空间复杂度:O(1)
滑动窗口
Python3 Code
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
复杂度
C++ Code:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
if(s.size()<p.size())
return {};
vector<int> c_p(26,0);
for(int i=0; i<p.size();i++)
c_p[p[i]-'a']++;
vector<int> flag_c(26,0);
int l = 0, r = 0, max_c = 0;
vector<int> res;
while(r<p.size())
{
int idx = s[r] - 'a';
flag_c[idx]++;
if(flag_c[idx]<=c_p[idx])
{
max_c++;
}
++r;
}
--r;
if(max_c==p.size())
res.push_back(0);
while(r<s.size()-1)
{
r++;
l++;
int idx_l = s[l-1] - 'a';
int idx_r = s[r] - 'a';
--flag_c[idx_l];
if(flag_c[idx_l]<c_p[idx_l])
{
--max_c;
}
++flag_c[idx_r];
if(flag_c[idx_r]<=c_p[idx_r])
{
++max_c;
}
if(max_c==p.size())
res.push_back(l);
}
return res;
}
};
复杂度分析
令 n 为数组长度。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if(s == null || s.length() < p.length()){
return new ArrayList<>();
}
List<Integer> res = new ArrayList<>();
int[] pCnt = new int[26];
for(int i = 0; i < p.length(); i++){
pCnt[p.charAt(i) - 'a']++;
}
//start和end分别控制窗口的开端和末端
int[] window = new int[26];
int start = 0, end = 0;
while(end < s.length()){
window[s.charAt(end) - 'a']++;
//维护一个长度固定的滑动窗口
if((end - start + 1) == p.length()){
//如果出现了符合条件的
if(Arrays.equals(pCnt, window)){
res.add(start);
}
//把左端的频率清除,左端向前加一
window[s.charAt(start) - 'a']--;
start++;
}
end++;
}
return res;
}数
}
时间复杂度:O(N) 空间复杂度:O(1)
var findAnagrams = function (s, p) { let res = [] if (s.length < p.length) return res let needsMap = new Map() for (let str of p) { needsMap.set(str, needsMap.has(str) ? needsMap.get(str) + 1 : 1) } let count = 0 let left = right = 0 let windowsMap = new Map() while (right < s.length) { let str = s[right] if (needsMap.has(str)) { windowsMap.set(str, windowsMap.has(str) ? windowsMap.get(str) + 1 : 1) if (windowsMap.get(str) == needsMap.get(str)) { count++ } } console.log(count); right++ if (right - left >= p.length) { // 说明批评到对应数量 if (count == needsMap.size) { res.push(left) } let first = s[left] if (needsMap.has(first)) { if (windowsMap.has(first)) { // 说明最左边在滑动的窗口中、得去掉 if (needsMap.get(first) == windowsMap.get(first)) { count-- } windowsMap.set(first, windowsMap.get(first) - 1) } } left++ } } 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" 的字母异位词。