leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 45 】2021-10-24 - 438. 找到字符串中所有字母异位词 #62

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

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" 的字母异位词。

Yufanzh commented 2 years ago

Intuition

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.

Algorithm in python3

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

Complexity Analysis:

okbug commented 2 years ago
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;
    }
};
zhangyalei1026 commented 2 years ago

思路

滑动窗口 + 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)

florenzliu commented 2 years ago

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:

zhangzz2015 commented 2 years ago

思路

关键点

代码

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;  
    }
};
zhuzuojun commented 2 years ago
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;
    }
}
learning-go123 commented 2 years ago

思路

代码

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 为数组长度。

carterrr commented 2 years ago
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;

    }

}
Zhang6260 commented 2 years ago

JAVA版本

思路:使用滑动窗口,构建滑动窗口有二种方式,本文采用的是使用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;
   }
}

时间复杂度:O(n)

空间复杂度:O(1)

ginnydyy commented 2 years ago

Problem

https://leetcode.com/problems/find-all-anagrams-in-a-string/

Notes

Solution

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;
    }
}

Complexity

RocJeMaintiendrai commented 2 years ago

题目

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)

XinnXuu commented 2 years ago

思路

滑动窗口长度为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;
    }
}

复杂度分析

zhiyuanpeng commented 2 years ago
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
asterqian commented 2 years ago

思路

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;
    }
};

复杂度分析

时间复杂度: O(N)
空间复杂度: O(1) 26个字母,所以为常数时间
Huangxuang commented 2 years ago

题目:[438. Find All Anagrams in a String](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)

思路

代码

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;
}

复杂度分析

mixtureve commented 2 years ago

思路

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;
    }
}

复杂度分析

zol013 commented 2 years ago

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)
ChiaJune commented 2 years ago

代码

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)

jiahui-z commented 2 years ago

思路: 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)

Bochengwan commented 2 years ago

思路

每次找到一个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

复杂度分析

naomiwufzz commented 2 years ago

思路:双指针+哈希

用一个定长的窗口扫一遍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

复杂度分析

Menglin-l commented 2 years ago

1. 用长度为26的数组记录字母出现的次数。

2. 滑窗


代码部分:

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;
    }
}

复杂度:

Time: O(N)

Space: O(1),额外的数组为常数级别

zszs97 commented 2 years ago

开始刷题

题目简介

【Day 45 】2021-10-24 - 438. 找到字符串中所有字母异位词

题目思路

题目代码

代码块

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;
    }
};

复杂度

lihanchenyuhua commented 2 years ago

class Solution { public: vector findAnagrams(string s, string p) { int n=s.size(),m=p.size(); if(m>n)return {}; vector 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; } };

carsonlin9996 commented 2 years ago

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;

    }
}
saltychess commented 2 years ago

思路

参考官方解法

代码

语言: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;
    }
}
kennyxcao commented 2 years ago

438. Find All Anagrams in a String

Intuition

Code

/**
 * @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;
};

Complexity Analysis

freedom0123 commented 2 years ago
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;
    }

};
m-z-w commented 2 years ago
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)

ysy0707 commented 2 years ago

思路:滑动窗口

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)

15691894985 commented 2 years ago

【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

复杂度分析:

ziyue08 commented 2 years ago

设置表示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;
};
RonghuanYou commented 2 years ago
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
shamworld commented 2 years ago
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;
};
AruSeito commented 2 years ago
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;
};
JinhMa commented 2 years ago

class Solution { public List findAnagrams(String s, String p) { int n = s.length(), m = p.length(); List 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'] ++;
    }

    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;
}

}

peteruixi commented 2 years ago

思路

滑动窗口原理 窗口内的字符由一个分开的列表管理

代码

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)

Richard-LYF commented 2 years ago

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
HouHao1998 commented 2 years ago

思想

滑动窗口 哈希

代码

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;
    }

复杂度分析

BpointA commented 2 years ago

思想

滑动窗口+列表

代码

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)

StefanLeeee commented 2 years ago

思路

滑动窗口

代码

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;
    }

复杂度

july-aha commented 2 years ago
 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)

kkwu314 commented 2 years ago

思路

滑动窗口+数组记录

代码

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)

lilixikun commented 2 years ago

思路

哈希表 + 滑动窗口

代码

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
};

复杂度

Tomtao626 commented 2 years ago

思路

JK1452470209 commented 2 years ago

思路

滑动窗口+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)

shawncvv commented 2 years ago

思路

滑动窗口

代码

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

复杂度

for123s commented 2 years ago

思路

关键点

代码

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 为数组长度。

flame0409 commented 2 years ago
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)

sxr000511 commented 2 years ago

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 };