LeetCode 438. Find All Anagrams in a String--字符串-滑动窗口--C++,Python解法


LeetCode题解专栏:LeetCode题解
LeetCode 所有题目总结:LeetCode 所有题目总结
大部分题目C++,Python,Java的解法都有。


题目地址:Find All Anagrams in a String - LeetCode


Given a string s and a non-empty string p, find all the start indices of p’s anagrams in s.

Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.

The order of output does not matter.

Example 1:

Input:
s: "cbaebabacd" p: "abc"

Output:
[0, 6]

Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".

Example 2:

Input:
s: "abab" p: "ab"

Output:
[0, 1, 2]

Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".

这道题目的意思很容易懂,就是看s的字母是否是p字符串的排列组合。如果把p字符串的所有可能都列出来,太多了,于是我想到使用counter。

Python解法如下:

from collections import Counter
class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        p_set = dict(Counter(p))
        lens = len(s)
        lenp = len(p)
        if lens < lenp:
            return res
        for i in range(0, lens-lenp+1):
            s_set = dict(Counter(s[i:i+lenp]))
            if s_set == p_set:
                res.append(i)
        return res

但上面的原始的Python解法会超时,因为有很多重复的工作,使用滑动窗口来减少工作量:

from collections import Counter
class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        p_set = Counter(p)
        lens = len(s)
        lenp = len(p)
        if lens < lenp:
            return res
        s_set = Counter(s[0:lenp-1])
        for i in range(lenp-1, lens):
            s_set[s[i]] += 1  # include a new char in the window
            start = i-lenp+1
            if s_set == p_set:
                res.append(start)
            s_set[s[start]] -= 1 # decrease the count of oldest char in the window
            if s_set[s[start]] == 0:
                del s_set[s[start]]  # remove the count if it is 0
        return res

上面的解法可以通过了,但可以更快,因为中间还是做了很多无用功,如果能快速跳过不存在的字符,可以更快。
更快的解法:

from collections import Counter


class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        pkey = set(p)
        p_set = Counter(p)
        lens = len(s)
        lenp = len(p)
        if lens < lenp:
            return res
        s_set = Counter(s[0:lenp-1])
        i = lenp-1
        while i < lens:
            if s[i] not in pkey:
                start = i+1
                i = start+lenp-1
                if i >= lens:
                    break
                s_set = Counter(s[start:i])
                continue
            s_set[s[i]] += 1  # include a new char in the window
            start = i-lenp+1
            if s_set == p_set:
                res.append(start)
            # decrease the count of oldest char in the window
            s_set[s[start]] -= 1
            if s_set[s[start]] == 0:
                del s_set[s[start]]  # remove the count if it is 0
            i += 1
        return res

C++解法如下:

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> pv(26,0), sv(26,0), res;
        if(s.size() < p.size())
           return res;
        // fill pv, vector of counters for pattern string and sv, vector of counters for the sliding window
        for(int i = 0; i < p.size(); ++i)
        {
            ++pv[p[i]-'a'];
            ++sv[s[i]-'a'];
        }
        if(pv == sv)
           res.push_back(0);

        //here window is moving from left to right across the string. 
        //window size is p.size(), so s.size()-p.size() moves are made 
        for(int i = p.size(); i < s.size(); ++i) 
        {
             // window extends one step to the right. counter for s[i] is incremented 
            ++sv[s[i]-'a'];
            
            // since we added one element to the right, 
            // one element to the left should be discarded. 
            //counter for s[i-p.size()] is decremented
            --sv[s[i-p.size()]-'a']; 

            // if after move to the right the anagram can be composed, 
            // add new position of window's left point to the result 
            if(pv == sv)  // this comparison takes O(26), i.e O(1), since both vectors are of fixed size 26. Total complexity O(n)*O(1) = O(n)
               res.push_back(i-p.size()+1);
        }
        return res;
    }
};

你可能感兴趣的:(LeetCode,c++-做题,python-做题)