自学内容网 自学内容网

滑动窗口_找出字符串中所有字母异位词、串联所有单词的子串_C++

滑动窗口_找出字符串中所有字母异位词、串联所有单词的子串_C++


1. 题目解析


leetcode链接:https://leetcode.cn/problems/VabMRr/

给定两个字符串 sp,找到 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" 的变位词。

2. 算法分析


1. 暴力解法

  • 首先能判断子串是否是变位词,这可以通过哈希表实现,因为只要是变位词,他们每个字母的数量都是一样的。(以示例1为例)

在这里插入图片描述

  • leftright的初始位置如图,之后不断将right右移,并且将right指向的元素加入哈希表(记为hash)。
  • right走到e时,发现e不在字符串p形成的哈希表中(记为hash_p),需要返回,直接让right返回,left++即可。同时right - left = p.size(),构成了一个变位词。

在这里插入图片描述

  • 另一种需要返回right的情况是,当left指向第二个b,right指向第三个b时,此时将right指向的元素加入hash,发现hash中该元素的数量大于hash_p中该元素的数量,此时也是需要返回的。

在这里插入图片描述

2. 滑动窗口

  • 在暴力解法的基础上,优化right的返回:
    • 当出现right指向的元素不在hash_p的情况时,直接让left移动到right的位置即可,同时清空hash
    • 当出现right指向的元素在hash中的数量,超过hash_p时,不需要移动right,只需要让left不断++,然后更改hash中的元素数量,直到hash中该元素的数量不再大于hasp_p即可。

3. 代码实现


1. 暴力解法

  • 会超时。
class Solution {
public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> ret;
        int n = s.size();
        int m = p.size();
        unordered_map<char, int> hash_p;    // 由字符串p形成的哈希表
        unordered_map<char, int> hash;

        for (auto e : p)
            hash_p[e]++;

        for (int left = 0, right = 0; right <= n; right++)
        {
            if (right == n) // 处理边界情况
            {
                if (right - left == m) 
                   ret.push_back(left);
                break;
            }
             
            hash[s[right]]++;
            if (!hash_p.count(s[right]) || hash[s[right]] > hash_p[s[right]])
            {
                if (right != n && right - left == m) ret.push_back(left);
                right = left;
                left++;
                hash.clear();
            }
        }
        
        return ret;
    }
};

2. map版

class Solution {
public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> ret;
        int n = s.size();
        int m = p.size();
        unordered_map<char, int> hash_p;    // 由字符串p形成的哈希表
        unordered_map<char, int> hash;

        for (auto e : p)
            hash_p[e]++;

        for (int left = 0, right = 0; right <= n; right++)
        {    
            // 处理边界情况
            if (right == n)
            {
                if (right - left == m) 
                {
                    ret.push_back(left);
                }
                break;
            }
            
            hash[s[right]]++;   // 进窗口
            if (!hash_p.count(s[right]) || hash[s[right]] > hash_p[s[right]])
            {
                if (right != n && right - left == m) ret.push_back(left); // 更新结果
                // 出窗口
                if (!hash_p.count(s[right]))
                {
                    left = right + 1;
                    hash.clear();
                }
                else if (hash[s[right]] > hash_p[s[right]])
                {
                    while (hash[s[right]] > hash_p[s[right]])
                    {
                        hash[s[left]]--;
                        left++;
                        if (hash[s[left]] == 0)
                            hash.erase(s[left]);
                    }
                }
            }
        }
        
        return ret;
    }
};

3. 数组模拟哈希(最快)

class Solution {
public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> ret;
        int n = s.size();
        int m = p.size();
        int hash_p[26] = {0};
        int hash[26] = {0};

        for (auto e : p)
            hash_p[e - 'a']++;

        for (int left = 0, right = 0; right <= n; right++)
        {    
            // 处理边界情况
            if (right == n)
            {
                if (right - left == m) 
                {
                    ret.push_back(left);
                }
                break;
            }

            hash[s[right] - 'a']+=1;   // 进窗口
            if (!hash_p[s[right] - 'a'] || hash[s[right] - 'a'] > hash_p[s[right] - 'a'])
            {
                if (right != n && right - left == m) ret.push_back(left); // 更新结果
                // 出窗口
                if (!hash_p[s[right] - 'a'])
                {
                    left = right + 1;
                    for (int i = 0; i < 26; i++) hash[i] = 0;
                }
                else if (hash[s[right] - 'a'] > hash_p[s[right] - 'a'])
                {
                    while (hash[s[right] - 'a'] > hash_p[s[right] - 'a'])
                    {
                        hash[s[left] - 'a']--;
                        left++;
                    }
                }
            }
        }
        
        return ret;
    }
};

4. 举一反三:串联所有单词的子串


leetcode链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/description/

class Solution {
public:
    string getStr(string s, int pos, int size)
    {
        string tmp;
        for (int i = 0; i < size; i++)
        {
            tmp += s[pos++];
        }
        return tmp;
    }

    vector<int> findSubstring(string s, vector<string>& words) 
    {
        vector<int> ret;
        int n = s.size(), m = words.size(), size = words[0].size();

        unordered_map<string, int> hash_w;
        unordered_map<string, int> hash;

        for (auto e : words)
            hash_w[e]++;

        // 一共滑动size次
        for (int i = 0; i < size; i++)
        {
            hash.clear();   // 细节
            for (int left = i, right = i; ; right += size)
            {
                // 边界情况
                if (right + size > n)
                {
                    if ((right - left) / size == m && (right - left) % size == 0)
                        ret.push_back(left);    // 更新结果
                    break;
                }

                string tmp = getStr(s, right, size);
                hash[tmp]++;     // 进窗口
                if (!hash_w.count(tmp) || hash[tmp] > hash_w[tmp])
                {
                    if (right != n && (right - left) / size == m && (right - left) % size == 0)
                        ret.push_back(left);    // 更新结果
                
                    // 出窗口
                    if (!hash_w.count(tmp))
                    {
                        left = right + size;
                        hash.clear();
                    }
                    else if (hash[tmp] > hash_w[tmp])
                    {
                        while (hash[tmp] > hash_w[tmp])
                        {
                            string tmp2 = getStr(s, left, size); // 细节
                            hash[tmp2]--;
                            left += size;
                            if (hash.count(tmp2) == 0)
                                hash.erase(tmp2);
                        }
                    }
                }
            }
        }

        return ret;
    }
};


原文地址:https://blog.csdn.net/weixin_73870552/article/details/142719198

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!