Open azl397985856 opened 2 years ago
class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: ans = [] n = len(s) m = len(p) a = [0] 26 aa = [0] 26 if n<m: return ans for i in range(m): a[ord(s[i])-ord('a')] += 1 aa[ord(p[i])-ord('a')] += 1
if a == aa:
ans.append(0)
for i in range(m,n):
a[ord(s[i])-ord('a')] += 1
a[ord(s[i - m])-ord('a')] -= 1
if aa == a:
ans.append(i - m + 1)
return ans
滑动窗口,维护一个长度为 len(p) 的窗口,扫描 s,记录窗口内每个字母的数量,与 p 中的字母数量比对,相同则为异位词。
时间复杂度 O(len(s))
空间复杂度 O(1) 因为只有小写字母,所以,只需要常数个空间列表就可以记录窗口。
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
pattern = [0] * 26
window = [0] * 26
for c in p:
pattern[ord(c) - ord('a')] += 1
pn = len(p)
ans = []
l = r = 0
while r < len(s):
window[ord(s[r]) - ord('a')] += 1
if r - l + 1 >= pn:
if window == pattern:
ans.append(l)
window[ord(s[l]) - ord('a')] -= 1
l += 1
r += 1
return ans
思路: 异位词的概念 可以利用数组哈希表表示。 只要维护一个滑动窗口,由于是相同则添加入答案数组。 所以,一旦不满足搜索条件,则左移。 一旦达到窗口长度,则添加值。
func findAnagrams(s string, p string) []int {
out := []int{}
if len(s) < len(p){
return out
}
var have,find [26]int
for i := range p{
have[p[i]-'a']++
}
l := 0
for r := range s{
find[s[r]-'a']++ //一遍没有AC的地方在忽视的每次右移的条件!
for find[s[r]-'a'] > have[s[r]-'a']{
find[s[l]-'a']--
l++
}
if r-l+1 == len(p){
out = append(out,l)
}
}
return out
}
时间复杂度:O(N) 空间复杂度:O(K) K 为字符串P的长度
# 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
C++ Code:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
/// sliding winwdow. // only lower case.
vector<int> ret;
if(s.size()<p.size())
return ret;
vector<int> smap(26,0);
vector<int> pmap(26,0);
for(int i=0; i< p.size(); i++)
pmap[p[i]-'a']++;
int count =0 ;
for(int i =0; i< p.size(); i++)
{
int index = s[i]-'a';
smap[index]++;
if(smap[index]<=pmap[index])
count++;
}
if(count == p.size())
ret.push_back(0);
for(int i=p.size(); i< s.size(); i++)
{
if(s[i]!=s[i-p.size()])
{
int index = s[i]-'a';
smap[index]++;
if(smap[index]<=pmap[index])
count++;
index = s[i-p.size()]-'a';
smap[index]--;
if(smap[index]<pmap[index])
count--;
}
if(count == p.size())
ret.push_back(i-p.size()+1);
}
return ret;
}
};
class Solution:
# 哈希+滑动窗口:用dict作为滑动窗口的容器,每次移动窗口时调整dict元素个数,并对比字符串p对应的哈希。
# 复杂度$O(abs(s-p)*26)$。
def findAnagrams1(self, s: str, p: str) -> List[int]:
n = len(p)
if n > len(s):
return []
cnt = Counter(p)
cur = Counter(s[:n])
ans = []
if cur == cnt:
ans += [0]
for i in range(n, len(s)):
cur[s[i]] += 1
cur[s[i - n]] -= 1
if cur[s[i - n]] == 0:
cur.pop(s[i - n])
if cur == cnt:
ans.append(i - n + 1)
return ans
# 哈希+滑动窗口:上述方法改进,只用一个dict解决问题。复杂度$O(abs(s-p)*26)$。
def findAnagrams2(self, s: str, p: str) -> List[int]:
n = len(p)
if n > len(s):
return []
cnt = Counter(p)
ans = []
for i in range(len(s)):
if i < n:
cnt[s[i]] -= 1
if cnt[s[i]] == 0:
del cnt[s[i]]
else:
cnt[s[i - n]] += 1
cnt[s[i]] -= 1
if cnt[s[i - n]] == 0:
del cnt[s[i - n]]
if cnt[s[i]] == 0:
del cnt[s[i]]
if len(cnt) == 0:
ans += [i - n + 1]
return ans
# 列表+滑动窗口:上述改进,哈希在对比前需要把计数为0的key丢掉,影响速度,换成定长26的list速度快。
# 复杂度$O(abs(s-p)*26)$
def findAnagrams(self, s: str, p: str) -> List[int]:
n = len(p)
if n > len(s):
return []
cnt = [0] * 26
cur = [0] * 26
for i in range(n):
cnt[ord(p[i]) - 97] += 1
cur[ord(s[i]) - 97] += 1
ans = []
if cur == cnt:
ans += [0]
for i in range(n, len(s)):
cur[ord(s[i]) - 97] += 1
cur[ord(s[i - n]) - 97] -= 1
if cur == cnt:
ans.append(i - n + 1)
return ans
# 列表+滑动窗口:上述改进,只需要一个list就可以搞定,速度稍慢,复杂度$O(abs(s-p)*26)$
def findAnagrams4(self, s: str, p: str) -> List[int]:
n = len(p)
cnt = [0] * 26
for i in range(n):
cnt[ord(p[i]) - 97] += 1
ans = []
for i in range(len(s)):
cnt[ord(s[i]) - 97] -= 1
if i >= n:
cnt[ord(s[i - n]) - 97] += 1
if not any(cnt):
ans.append(i - n + 1)
return ans
class Solution {
public List
int l = 0, r = p.length();
while (l < s.length() && l < p.length()) {
window[s.charAt(l) - 'a']++;
l++;
}
l = 0;
while (l < s.length() - p.length() + 1) {
if (Arrays.equals(window, charp)) {
list.add(l);
}
if (r < s.length()) {
window[s.charAt(r++) - 'a']++;
}
window[s.charAt(l++) - 'a']--;
}
return list;
}
}
class Solution(object): def findAnagrams(self, s, p): """ :type s: str :type p: str :rtype: List[int] """ need = collections.Counter(p) window = collections.defaultdict(int)
l = r = 0
valid = 0
res = []
while r < len(s):
c = s[r]
r += 1
if c in need:
window[c] += 1
if window[c] == need[c]:
valid += 1
print(l, r)
while r - l + 1 > len(p):
if valid == len(need):
res.append(l)
d = s[l]
l += 1
if d in need:
if window[d] == need[d]:
valid -= 1
window[d] -= 1
return res
Java Code:
public class Solution {
/**
* @param s a string
* @param p a non-empty string
* @return a list of index
*/
public List<Integer> findAnagrams(String s, String p) {
// Write your code here
List<Integer> ans = new ArrayList <Integer>();
int[] sum = new int[30];
int plength = p.length(), slength = s.length();
for(char c : p.toCharArray()){
sum[c - 'a'] ++;
}
int start = 0, end = 0, matched = 0;
while(end < slength){
if(sum[s.charAt(end) - 'a'] >= 1){
matched ++;
}
sum[s.charAt(end) - 'a'] --;
end ++;
if(matched == plength) {
ans.add(start);
}
if(end - start == plength){
if(sum[s.charAt(start) - 'a'] >= 0){
matched --;
}
sum[s.charAt(start) - 'a'] ++;
start ++;
}
}
return ans;
}
}
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
if len(p) > len(s):
return []
p = sorted(p)
res = []
Map = {}
for i in range(len(s)-len(p) + 1):
s1 = s[i: i+len(p)]
if s1 in Map: #如果已经在map里,说明是要找的,直接加1,下面不再重复判断
Map[s1] += 1
res.append(i)
continue
if sorted(s1) == p:
res.append(i)
Map[s1] = 1
return res
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 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" 的异位词。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int[] originString = new int[26];
int[] subString = new int[26];
List<Integer> res = new ArrayList<>();
int lengthOfP = p.length();
int lengthOfS = s.length();
if(lengthOfP > lengthOfS){
return res;
}
for(int i = 0; i < lengthOfP; i++){
originString[p.charAt(i) - 'a'] ++;
}
//generate the slide
for(int i = 0; i < lengthOfP; i++){
subString[s.charAt(i) - 'a'] ++;
}
if(isEqual(originString, subString)){
res.add(0);
}
for(int start = 0, end = lengthOfP; end < lengthOfS;){
subString[s.charAt(end++) - 'a'] ++;
subString[s.charAt(start++) - 'a'] --;
if(isEqual(originString, subString)){
res.add(start);
}
}
return res;
}
public boolean isEqual(int[] originString, int[] subString){
for(int i = 0; i < originString.length; i++){
if(originString[i] != subString[i]){
return false;
}
}
return true;
}
}
时间复杂度:$O(n^2)$
空间复杂度:$O(n)$
你的滑动窗口写的太麻烦了,没必要维护一个和p一样大的滑动窗口,只要这个窗口小于等于p就行,一旦某个元素个数比p中的还要多,就要不断抛弃左端元素,一直到两者相等,甚至有可能全部抛弃光
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int[] originString = new int[26];
int[] subString = new int[26];
List<Integer> res = new ArrayList<>();
int lengthOfP = p.length();
int lengthOfS = s.length();
for(int i = 0; i < lengthOfP; i++){
originString[p.charAt(i) - 'a'] ++;
}
int left = 0;
for(int right = 0; right < lengthOfS; right++){
//把right对应字母加进来
int curRightCharacter = s.charAt(right) - 'a';
subString[curRightCharacter] ++;
//如果subString中某个字母的个数比originString要大
//那么说明当前的子窗口要抛弃元素,一直抛弃到当前right指向的元素和originString中的个数相等
while(subString[curRightCharacter] > originString[curRightCharacter]){
//看当前最左端字母是多少,把它抛弃掉
int curLeftCharacter = s.charAt(left) - 'a';
subString[curLeftCharacter] --;
left++;
}
//出了这个循环后,我们能保证当前子串中存在的字母个数和originalString中的都相等
//如果两个串长度又相等,那么当前的left就要加进结果集
if(right - left + 1 == lengthOfP){
res.add(left);
}
}
return res;
}
}
时间复杂度:$O(n)$
空间复杂度:$O(1)$
class Solution {
public List
if (sLen < pLen) {
return new ArrayList<Integer>();
}
List<Integer> ans = new ArrayList<Integer>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < pLen; ++i) {
++sCount[s.charAt(i) - 'a'];
++pCount[p.charAt(i) - 'a'];
}
if (Arrays.equals(sCount, pCount)) {
ans.add(0);
}
for (int i = 0; i < sLen - pLen; ++i) {
--sCount[s.charAt(i) - 'a'];
++sCount[s.charAt(i + pLen) - 'a'];
if (Arrays.equals(sCount, pCount)) {
ans.add(i + 1);
}
}
return ans;
}
}
Algo
- EZ write, lapsa time/space: Counter(p), Counter(curr), compare
- Opt Solu:
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
l = 0
n = len(p)
target = collections.Counter(p)
res = []
while l <= len(s) - n:
if collections.Counter(s[l:(l + n)]) == target: res.append(l)
l += 1
return res
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
# in code below, manipulate target and only target
target = collections.Counter(p)
res = []
for i in range(len(s)):
# if i >= len(p), we should "rebalance the credit" of the dropped char in the past window
# which should not be counted into this window
if i >= len(p):
target[s[i - len(p)]] += 1
# if there is no "credit" after "rebalance"
# this char should not be counted into the anagram counting
# we delete it in advance
if target[s[i - len(p)]] == 0: del target[s[i - len(p)]]
# 1. every time encounter a char, set counter[char] -= 1
target[s[i]] -= 1
# 2. if no "credit" for current char, delete the "account" in target
if target[s[i]] == 0: del target[s[i]]
# 3. if all "account" in target has been canceled, that means a match occurs, add this window
if len(target) == 0: res.append(i - len(p) + 1)
return res
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
from collections import defaultdict
target_dict = defaultdict(int)
for char in p:
target_dict[char] -= 1
lenp = len(p)
lens = len(s)
for char in s[:lenp]:
if char in target_dict:
target_dict[char] += 1
result = []
differ = sum([int(x!=0) for x in list(target_dict.values())])
if differ == 0:
result.append(0)
for idx in range(lenp,lens):
out_char = s[idx - lenp]
in_char = s[idx]
if out_char in target_dict:
target_dict[out_char] -= 1
if target_dict[out_char] == 0:
differ -= 1
elif target_dict[out_char] == -1:
differ += 1
if in_char in target_dict:
target_dict[in_char] += 1
if target_dict[in_char] == 0:
differ -= 1
elif target_dict[in_char] == 1:
differ += 1
if differ == 0:
result.append(idx - lenp + 1)
return result
time complexity: O(M+N+K), M is length of s, N is length of p, K is the number of unique characters in p space complexity: O(K)
Code:
public class Solution {
public IList
IList<int> res = new List<int>();
if (s.Length < p.Length)
return res;
int[] sDict = new int[26];
int[] pDict = new int[26];
foreach(var ch in p)
pDict[ch - 'a']++;
int pLen = p.Length;
for (int i = 0; i < pLen; i++)
sDict[s[i] - 'a']++;
int cursoIndex = pLen;
for (; cursoIndex < s.Length; cursoIndex++)
{
if (IsEqual(sDict, pDict ))
res.Add(cursoIndex - pLen);
sDict[s[cursoIndex - pLen] - 'a']--;
sDict[s[cursoIndex] - 'a']++;
}
if (IsEqual(sDict, pDict ))
res.Add(cursoIndex - pLen);
return res;
}
public bool IsEqual(int[] sDict, int[] pDict)
{
for (int i = 0; i < sDict.Length ; i++)
{
if (sDict[i] != pDict[i])
return false;
}
return true;
}
}
代码
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int n = s.length(), m = p.length();
List<Integer> res = new ArrayList<>();
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']++;
sCnt[s.charAt(i) - 'a']++;
}
if(Arrays.equals(sCnt, pCnt)){
res.add(0);
}
for(int i = m; i < n; i++){
sCnt[s.charAt(i - m) - 'a']--;
sCnt[s.charAt(i) - 'a']++;
if(Arrays.equals(sCnt, pCnt)){
res.add(i - m + 1);
}
}
return res;
}
}
思路 Sliding Window
public List
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;
}
时间复杂度:O(n) 空间复杂度为 O(1)
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
Sliding window + HashTable. We keep a sliding window with the size of p. Then we use one hashtable to count the letters in current sliding window and another hashset to record all the letters in current sliding window also appears in p and its count is as same as the count in p. When the length of hashset is as same as the count of unique letters in p then we have one anagram.
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
p_count = collections.Counter(p)
cur_count = {}
cur_len = set()
i, j = 0, 0
ans = []
while i <= len(s) - len(p):
## When the length of current sliding window is one larger than the p
## we need to move the left side of the window one step
if j - i == len(p):
## In move the left side of the window, we minus the letter's count by one
cur_count[s[i]] -= 1
## We check whether it is in p
if s[i] in p_count:
## whether its count is as same as that in p
if p_count[s[i]] == cur_count[s[i]]:
## If so, it means the letter in current sliding window
## has same count in p. we add the letter to the hash set,
## we find a unique letter meeting conditions
cur_len.add(s[i])
## If the hash set has same unique letters as p
## we find one anagram we need
if len(cur_len) == len(p_count):
ans.append(i+1)
## If the count is not as same as in p, we remove this unique letter
else:
if s[i] in cur_len:
cur_len.discard(s[i])
i += 1
## We move the right side of the sliding window one step right till end of s
if j < len(s):
## Count the letter frequency in current sliding window
cur_count[s[j]] = cur_count.get(s[j], 0) + 1
## If the letter appearing in p and has same amount in p
## we find on unique letter
if s[j] in p_count and cur_count[s[j]] == p_count[s[j]]:
cur_len.add(s[j])
## If the letter appearing in p and doesn't same amount in p
## we need to remove this unique letter from set
else:
if s[j] in cur_len:
cur_len.discard(s[j])
## If the hash set has same unique letters as p
## we find one anagram we need
if len(cur_len) == len(p_count):
ans.append(i)
j += 1
return ans
Time: O(S +P) Space: O(S+P)
思路: 构建两个26个字母的数组,一个用于记录p中字母的情况,另外一个用于记录固定窗口中字母的情况。这里每次滑动窗口的时候,第二个数组只需改变两个地方即可。改变完通过比对两个数组是否相同来确定是否是异位词。
func findAnagrams(s string, p string) []int {
ans := make([]int,0)
if len(s) < len(p) {
return ans
}
var que, tem [26]int
for i := 0 ; i < len(p); i ++ {
que[p[i]-'a'] ++
tem[s[i]-'a'] ++
}
if que == tem {
ans = append(ans,0)
}
for i := len(p); i < len(s); i ++ {
tem[s[i - len(p)] - 'a'] --
tem[s[i] - 'a'] ++
if tem == que {
ans = append(ans,i - len(p) + 1)
}
}
return ans
}
时间复杂度:O(m+(n−m)×Σ) 空间复杂度:O(Σ)
利用滑动窗口
var findAnagrams = function (s, p) {
let pn = p.length;
let sn = s.length;
let hash = new Map();
let res = [];
for (let i = 0; i < pn; i++) {
hash.set(p.charAt(i), (hash.get(p.charAt(i)) || 0) + 1);
}
let l = 0;
let r = 0;
while (r < sn) {
if (hash.get(s.charAt(r)) !== undefined) {
hash.set(s.charAt(r), hash.get(s.charAt(r)) - 1);
}
while (r - l + 1 > pn) {
if (hash.get(s.charAt(l)) !== undefined) {
hash.set(s.charAt(l), hash.get(s.charAt(l)) + 1);
}
l++;
}
r++;
let pass = true;
for (let value of hash.values()) {
if (value) {
pass = false;
break;
}
}
pass && res.push(l);
}
return res;
};
时间复杂度:O(n+k)
空间复杂度:O(k)
var findAnagrams = function (s, t) {
// 需要的
let need = {};
// 窗口中的字符
let window = {};
for (let a of t) {
// 统计需要的字符
need[a] = (need[a] || 0) + 1;
}
// 左右指针
let left = 0,
right = 0;
let valid = 0;
let res = [];
while (right < s.length) {
// 即将移入窗口的字符
let c = s[right];
// 右移窗口
right++;
if (need[c]) {
// 当前字符在需要的字符中,则更新当前窗口统计
window[c] = (window[c] || 0) + 1;
if (window[c] == need[c]) {
// 当前窗口和需要的字符匹配时,验证数量增加1
valid++;
}
}
while (right - left >= t.length) {
if (valid == Object.keys(need).length) {
res.push(left);
}
let d = s[left];
left++;
if (need[d]) {
if (window[d] == need[d]) {
valid--;
}
window[d]--;
}
}
}
return res;
};
稍后补充,在整理
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
int m = s.length();
int n = p.length();
if (m < n) {
// 如果s没p长,直接返回空列表
return ans;
}
int[] sCnt = new int[26];
int[] pCnt = new int[26];
for (int i = 0; i < n; i++) {
sCnt[s.charAt(i) - 'a']++;
pCnt[p.charAt(i) - 'a']++;
}
if (Arrays.equals(sCnt, pCnt)) {
// 初始窗口就相等
ans.add(0);
}
for (int i = 0; i < m - n; i++) {
// 窗口右移
sCnt[s.charAt(i) - 'a']--;
sCnt[s.charAt(i + n) - 'a']++;
if (Arrays.equals(sCnt, pCnt)) {
ans.add(i + 1);
}
}
return ans;
}
}
使用滑动窗口来完成
class Solution { public List<Integer> findAnagrams(String s, String p) { List<Integer> res = new ArrayList<>(); int count = p.length(); int []dp = new int[26]; for(int i = 0 ; i < p.length() ; i++){ dp[p.charAt(i)-'a']++; } int temp = count; for(int i = 0; i < s.length(); i++ ){ //当小于滑动窗口 if(i<count-1){ if(dp[s.charAt(i)-'a']>0){ temp--; } dp[s.charAt(i)-'a']--; continue; } //处理滑动窗口的右边 if(dp[s.charAt(i)-'a']>0){ temp --; } dp[s.charAt(i)-'a']--; if(temp==0){ res.add(i-count+1); } //处理滑动窗口左边 if(dp[s.charAt(i-count+1)-'a']>=0){ temp++; } dp[s.charAt(i-count+1)-'a']++; } return res; } }
时间复杂度:O(n)
空间复杂度:O(1)
sliding win + array as map
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
map_p = {string:0 for string in string.ascii_lowercase }
map_s = {string:0 for string in string.ascii_lowercase }
res = []
# init map_p
for char in p:
map_p[char] += 1
for i in range(len(s)):
map_s[s[i]] += 1
if i >= len(p):# when the i > len(p), could mantain the win by del the count of the leftest.
map_s[s[i - len(p)]] -= 1
if map_p == map_s:
res.append(i - len(p) + 1)
return res
Time: O(n) Space: O(n)
定长滑动窗口基础题
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int n = s.length();
int m = p.length();
List<Integer> res = new ArrayList<>();
if ( n < m) {
return res;
}
int[] countS = new int[26];
int[] countP = new int[26];
for (int i=0; i<m; i++) {
countS[s.charAt(i) - 'a']++;
countP[p.charAt(i) - 'a']++;
}
for (int i=m; i<=n; i++) {
int k = 0;
for (; k<26; k++) {
if (countS[k] != countP[k]) {
break;
}
}
if (k == 26) {
res.add(i-m);
}
if (i != n) {
countS[s.charAt(i-m) - 'a']--;
countS[s.charAt(i) - 'a'] ++;
}
}
return res;
}
}
TC: O(n) SC: O(n-m)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<Integer>();
}
List<Integer> ans = new ArrayList<Integer>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < pLen; ++i) {
++sCount[s.charAt(i) - 'a'];
++pCount[p.charAt(i) - 'a'];
}
if (Arrays.equals(sCount, pCount)) {
ans.add(0);
}
for (int i = 0; i < sLen - pLen; ++i) {
--sCount[s.charAt(i) - 'a'];
++sCount[s.charAt(i + pLen) - 'a'];
if (Arrays.equals(sCount, pCount)) {
ans.add(i + 1);
}
}
return ans;
}
}
// sliding window
// TC = O(N_s + N_p), SC = O(1)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new LinkedList<Integer>();
int lenp = p.length();
int lens = s.length();
if( lens < lenp ) {
return ans;
}
int[] pCount = new int[26];
int[] sCount = new int[26];
// put p into arr pCount
for(int i = 0; i < lenp; i++) {
pCount[p.charAt(i) - 'a']++;
}
// sliding window on the string s
for(int i = 0; i < lens; i++) {
// move the sliding window
// add one more letter
sCount[s.charAt(i) - 'a']++;
if(i >= lenp) {
// remove one letter
sCount[s.charAt(i - lenp) - 'a']--;
}
if(Arrays.equals(pCount, sCount)) {
ans.add(i - lenp + 1);
}
}
return ans;
}
}
对于这种子串排列的问题, 一般用滑动窗口解决, 解决的时候处理好下面几个点就好了
right
指针(扩大窗口)right
指针的时候需要更新哪些数据left
指针(收缩窗口)left
指针的时候需要更新哪些数据var findAnagrams = function(s, p) {
const ans = []
if(p.length > s.length) { return ans }
const targetMap = new Map()
for(let i = 0; i < p.length; i++) {
targetMap.set(p[i], (targetMap.get(p[i]) || 0) + 1)
}
let left = right = valid = 0
let window = new Map()
while(right < s.length) {
const rightChar = s[right]
right++
const targetCount = targetMap.get(rightChar)
if (targetCount !== undefined) {
let curCount = (window.get(rightChar) || 0) + 1
window.set(rightChar, curCount)
if (curCount === targetCount) {
valid++
} else if (curCount > targetCount) {
// 需要收缩窗口, 直到curCount = targetCount
while(curCount > targetCount) {
const leftChar = s[left]
const leftCount = window.get(leftChar)
if (leftCount === targetMap.get(leftChar)) {
valid--
}
if (leftChar === rightChar) {
curCount--
}
window.set(leftChar, leftCount - 1)
left++
}
}
if (valid === targetMap.size) { ans.push(left) }
} else {
left = right
valid = 0
window = new Map()
}
}
return ans
};
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
'''
ans = []
i = 0
cnt1 = Counter(p)
cnt2 = {}
for j in range(len(s)):
cnt2[s[j]] = cnt2.get(s[j], 0) + 1
if j - i + 1 > len(p):
cnt2[s[i]] -= 1
if cnt2[s[i]] == 0:
del cnt2[s[i]]
i += 1
if cnt1 == cnt2:
ans.append(i)
return ans
'''
ans = []
i = 0
cnt1 = [0] * 26
cnt2 = [0] * 26
for char in p:
cnt1[ord(char) - ord('a')] += 1
for j in range(len(s)):
cnt2[ord(s[j]) - ord('a')] += 1
if j - i + 1 > len(p):
cnt2[ord(s[i]) - ord('a')] -= 1
i += 1
if cnt1 == cnt2:
ans.append(i)
return ans
滑动窗口 + 数组
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
len_s = len(s)
len_p = len(p)
ans = []
if len_s < len_p:
return ans
p_count = [0] * 26
s_count = [0] * 26
for i in range(len_p):
p_count[ord(p[i]) - ord('a')] += 1
left = 0
for right in range(len_s):
cur_right = ord(s[right]) - ord('a')
s_count[cur_right] += 1
while s_count[cur_right] > p_count[cur_right]:
cur_left = ord(s[left]) - ord('a')
s_count[cur_left] -= 1
left += 1
if right - left + 1 == len_p:
ans.append(left)
return ans
滑起来
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
int[] count = new int[26];
int n = s.length(), m = p.length();
for (int i = 0; i < m; i++){
count[p.charAt(i) - 'a']++;
}
int a = 0;
for(int i = 0; i < 26; i++){
if(count[i] != 0) a++;
}
for(int l = 0, r = 0, b = 0; r < n; r++){
if(--count[s.charAt(r) - 'a'] == 0){
b++;
}
if(r - l + 1 > m && ++count[s.charAt(l++) - 'a'] == 1){
b--;
}
if(a == b){
ans.add(l);
}
}
return ans;
}
}
复杂度分析
class Solution {
public List<Integer> findAnagrams(String s, String t) {
HashMap<Character, Integer> window = new HashMap<>();
HashMap<Character, Integer> need = new HashMap<>();
List<Integer> res = new ArrayList<>();
for (char t1 : t.toCharArray()) {
need.put(t1, need.getOrDefault(t1,0) + 1);
}
int left = 0; int right = 0; int valid = 0;
while (right < s.length()) { // 区间[left, right)是左闭右开的,所以初始情况下窗口没有包含任何元素:
char s1 = s.charAt(right);
right ++;
if (need.containsKey(s1)) {
window.put(s1,window.getOrDefault(s1,0) + 1);
if (window.get(s1).equals(need.get(s1))){
valid ++;
}
}
// 右指针移动当于在寻找一个「可行解」,然后移动左指针在优化这个「可行解」,最终找到最优解
while (right - left >= t.length()) {
if (valid == need.size()) res.add(left);
char s2 = s.charAt(left);
left ++;
if (need.containsKey(s2)) {s
if (window.get(s2).equals(need.get(s2))){ // equals not ==
valid --;
}
window.put(s2,window.get(s2) - 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:
// <字母码值, 个数>
unordered_map<int,int> m;
vector<int> findAnagrams(string s, string p) {
vector<int> re;
if(s.length()<p.length())return re;
for(auto const c : p){
m[c]++;
}
int pos1 =0;
int pos2 = 1;
while (pos2<=s.length()){
if(pos2<=p.length()){
if(!--m[s[pos2-1]]) {
m.erase(s[pos2-1]);
}
if(m.empty())re.push_back(pos1);
if(pos2==p.length())pos1++;
pos2++;
continue;
}
if(!--m[s[pos2-1]]) {
m.erase(s[pos2-1]);
}
if(!++m[s[pos1-1]]) {
m.erase(s[pos1-1]);
}
if(m.empty())re.push_back(pos1);
pos1++;
pos2++;
}
return re;
}
};
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
ls = [0] * 26
if len(s) < len(p):
return []
for c in p:
ls[ord(c) - ord('a')] += 1
ls1 = [0] * 26
l , r = 0 , 0
for _ in range(len(p)):
ls1[ord(s[r]) - ord('a')] += 1
r += 1
ans = []
if ls == ls1:
ans.append(0)
for i in range(0,len(s) - len(p)):
ls1[ord(s[i]) - ord('a')] -= 1
ls1[ord(s[i + len(p)]) - ord('a')] += 1
if ls == ls1:
ans.append(i+1)
return ans
import collections
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
#统计p中各个字母出现的次数
traget = collections.Counter(p)
ans = []
for i in range(len(s)):
if i >= len(p):
traget[s[i-len(p)]] += 1
#全部匹配完毕
if traget[s[i-len(p)]] == 0:
del traget[s[i-len(p)]]
traget[s[i]] -= 1
if traget[s[i]] ==0:
del traget[s[i]]
if len(traget) ==0:
ans.append(i-len(p)+1)
return ans
时间复杂度:O(n) 空间复杂度:O(1)
Sliding window
class Solution {
public List<Integer> findAnagrams(String s, String p) {
ArrayList<Integer> res = new ArrayList<>();
if(s.length() < p.length()){
return res;
}
int[] counts = new int[26];
int[] countp = new int[26];
for(char curr: p.toCharArray()){
countp[curr - 'a']++;
}
for(int i = 0; i < s.length(); i++){
counts[s.charAt(i) - 'a']++;
if(i >= p.length()){
counts[s.charAt(i - p.length()) - 'a']--;
}
if(Arrays.equals(countp, counts)){
res.add(i - p.length() + 1);
}
}
return res;
}
}
复杂度分析
滑动窗口
func findAnagrams(s string, p string) (ans []int) {
hashmapa:= map[uint8]int{}
for i := 0; i < len(p); i++ {
hashmapa[p[i]]++
}
hashmapb:= map[uint8]int{}
for k, v:=range hashmapa{
hashmapb[k] = v
}
l:=0
for j:=0;j<len(s);j++{
if _,ok:=hashmapa[s[j]];ok{
hashmapb[s[j]]--
if hashmapb[s[j]]==0{
delete(hashmapb, s[j])
}
if j-l>=len(p){
hashmapb[s[l]]++
if hashmapb[s[l]] == 0{
delete(hashmapb, s[l])
}
l++
}
if len(hashmapb) == 0{
ans = append(ans, l)
}
}else {
for k := l;k<j;k++{
hashmapb[s[k]]++
}
l = j+1
if l>=len(s){
break
}
}
}
return
}
时间:O(n) 空间:O(k)k为s长度
滑动窗口
var findAnagrams = function(s, p) {
let need = {}
let win = {}
for(let a of p) {
need[a] = (need[a] || 0) + 1
}
let left = 0, right = 0
let val = 0
let res = []
while (right < s.length) {
let c = s[right]
right++
if(need[c]) {
win[c] = (win[c] || 0) + 1
if(win[c] == need[c]) {
val++
}
}
while(right - left >= p.length) {
if(val == Object.keys(need).length) {
res.push(left)
}
let d = s[left]
left++
if(need[d]) {
if(win[d] == need[d]) {
val--
}
win[d]--
}
}
}
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> 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;
}
}
思路
用固定宽度的滑动窗口来做
复杂度
代码(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 l = 0;
for (int r = 0; r < s.length(); ++r) {
dictA[s[r] - 'a']++;
if (r >= p.length()) {
dictA[s[l] - 'a']--;
++l;
}
if (dictA == dictP)
res.push_back(l);
}
return res;
}
};
var findAnagrams = function(s, t) {
let need = {};
let window = {};
for (let a of t) {
need[a] = (need[a] || 0) + 1;
}
let left = 0,
right = 0;
let valid = 0;
let res = [];
while (right < s.length) {
let c = s[right];
right++;
if (need[c]) {
window[c] = (window[c] || 0) + 1;
if (window[c] == need[c]) {
valid++;
}
}
while (right - left >= t.length) {
if (valid == Object.keys(need).length) {
res.push(left);
}
let d = s[left];
left++;
if (need[d]) {
if (window[d] == need[d]) {
valid--;
}
window[d]--;
}
}
}
return res;
};
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
cnt = [0]*26
for char in p:
cnt[ord(char) - ord('a')] += 1
cur = [0]*26
ans = []
i = 0
for j in range(len(s)):
cur[ord(s[j]) - ord('a')] += 1
if j - i + 1 > len(p):
cur[ord(s[i]) - ord('a')] -= 1
i += 1
if j - i + 1 == len(p) and cur == cnt:
ans.append(i)
return ans
Time complexity O(n) Space complexity O(1)
滑动窗口
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
need, window = {}, {}
for c in p:
need[c] = need[c] + 1 if c in need else 1
window[c] = 0
l, r, valid = 0, 0, 0
m, n = len(s), len(p)
res = []
while(r < m):
c = s[r]
if c in window:
window[c] += 1
if window[c] == need[c]:
valid += 1
if r - l == n - 1:
if valid == len(need):
res.append(l)
d = s[l]
if d in window:
if window[d] == need[d]:
valid -= 1
window[d] -= 1
l += 1
r += 1
return res
复杂度分析
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
var findAnagrams = function (s, p) {
let arr = [];
for (let char of p) {
let charCode = char.charCodeAt()-97;
if (arr[charCode] === undefined) {
arr[charCode] = 1;
} else {
arr[charCode] += 1;
}
}
let ans = [];
for (let i = 0; i < s.length - p.length + 1; i++) {
let substr = s.slice(i, i + p.length);
let temp = [];
for (let char of substr) {
let charCode = char.charCodeAt()-97;
if (temp[charCode] === undefined) {
temp[charCode] = 1;
} else {
temp[charCode] += 1;
}
}
let flag = true;
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== temp[i]) {
flag = false;
break;
}
}
if (flag) {
ans.push(i);
}
}
return ans;
};
思路
用哈希表保存p长度窗口的字符差异个数。
代码
var findAnagrams = function(s, p) {
const lenP = p.length;
const lenS = s.length;
if(lenS < lenP) return [];
let map = new Map();
let diff = 0;
let res = [];
for(let i = 0; i < lenP; i++){
map.set(p[i], (map.get(p[i]) || 0) - 1);
map.set(s[i], (map.get(s[i]) || 0) + 1);
};
for(let value of map.values()){
if(value !== 0) diff++;
}
if(diff === 0) res.push(0);
for(let i = 1; i < lenS - lenP + 1; i++){
let charIn = s[i + lenP - 1];
let charOut = s[i - 1];
if(!map.has(charIn) || map.get(charIn) === 0) diff++;
else if(map.get(charIn) === -1) diff--;
map.set(charIn, (map.get(charIn) || 0) + 1);
if(map.get(charOut) === 1) diff--;
else if(map.get(charOut) === 0) diff++;
map.set(charOut, map.get(charOut) - 1);
if(diff === 0) res.push(i);
};
return res;
};
复杂度分析
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;
}
}
class Solution: def findAnagrams(self, s: str, p: str) -> List[int]: '''' hint: char is also represented as int in ASCII 'a': 97 'b': 98 'c': 99 ''' k = len(p) p_sum = 0 for i in range(k): p_sum += ord(p[i])
curr = 0 ## store the sum of int for every p char
ans = []
## do sliding window
for i in range(len(s)):
curr += ord(s[i])
if i >= k-1:
if curr == p_sum: ## find the start index
ans.append(i-k+1)
curr -= ord(s[i-k+1]) ## i-k+1 index falls out of window
return ans
time: o(n) space: o(n)
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" 的字母异位词。