愆蟠唉 发表于 2025-5-30 20:14:05

09 滑动窗口:找到字符串中所有字母异位词 438

2025-05-25 10:41:38 星期日

目录

[*]1456.定长子串中元音的最大数目

[*]说一下我最开始的思路

[*]如何进一步优化呢
[*]让我们看一看灵神的做法
[*]438 找到字符串中所有字母异位词

[*]再试一下
[*]学习大神的思路和方法

[*]定长滑动窗口
[*]继续优化



[========]
1456.定长子串中元音的最大数目

说一下我最开始的思路

点击查看代码class Solution {
public:
    int maxVowels(string s, int k) {
      if (s.empty() || k == 0)
            return 0;
      unordered_set<char> st {'a', 'e', 'i', 'o', 'u'};
      //定长滑动窗口
      /*
            时间复杂度:O(nk)
            解释:外循环遍历n-k+1次,n是字符串长度
                  内循环遍历k次
            如果k很大,那就是O(n^2)
            Leecode会超时
            空间复杂度:O(1)
      */
      int left = 0, right = k;
      int ans = INT_MIN;
      int cnt = 0;
      while (left < s.length() - k + 1) { //nk
            for (int i = left; i < right; ++i) {
                if (st.contains(s))
                  cnt++;
            }
            ans = max(ans, cnt);
            cnt = 0;
            ++left, ++right;
      }
      return ans;
    }
};如何进一步优化呢

我的想法是,内循环没有必要每次都检查已经重复的部分,因为定长滑动窗口滑动时只会去掉最左边的元素,加上最右边的元素,只要检查这两个就好了。
点击查看代码```C++class Solution {public:    int maxVowels(string s, int k) {      if (s.empty() || k == 0)            return 0;      unordered_set st {'a', 'e', 'i', 'o', 'u'};      //通过了,时间复杂度优化到O(n)      //空间复杂度O(1)      int left = 0, right = k;      int ans = INT_MIN;      int cnt = 0;      for (int i = left; i < right; ++i) {            if (st.contains(s))                cnt++;      }      ans = max(ans, cnt);      while (left < s.length() - k + 1) {             //滑动窗口向右滑动            if (st.contains(s)) {                cnt++;            }            //去掉left的元素            if (st.contains(s)) {                cnt--;            }            ans = max(ans, cnt);            if (ans == k)                break;            ++left, ++right;            }      return ans;    }};```妈呀,整整用了一个多小时,我真是笨死了。
让我们看一看灵神的做法

灵神的思路和我第二种是一样的,但是灵神的代码更加简洁优雅
点击查看代码class Solution {
public:
    int maxVowels(string s, int k) {
      int ans = 0, vowel = 0;
      for (int i = 0; i < s.length(); i++) {
            // 1. 进入窗口
            if (s == 'a' || s == 'e' || s == 'i' || s == 'o' || s == 'u') {
                vowel++;
            }
            if (i < k - 1) { // 窗口大小不足 k
                continue;
            }
            // 2. 更新答案
            ans = max(ans, vowel);
            // 3. 离开窗口
            char out = s;
            if (out == 'a' || out == 'e' || out == 'i' || out == 'o' || out == 'u') {
                vowel--;
            }
      }
      return ans;
    }
};

作者:灵茶山艾府
链接:https://leetcode.cn/problems/maximum-number-of-vowels-in-a-substring-of-given-length/solutions/2809359/tao-lu-jiao-ni-jie-jue-ding-chang-hua-ch-fzfo/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。代码看完了,优雅,实在是太优雅了。
好了,这道题看完之后,我们可以来解决Leecode hot100上的异位词这道题了,这才是我们今天的目标(BOSS)
呜呜呜,一开始看这道题一个多小时愣是没写出来,怎么就难死我了呢?
[========]
438 找到字符串中所有字母异位词

再试一下

又是半个小时过去了。
额,我做不出来的原因是,如果这道题像刚才那样依然采用定长滑动窗口,那样每次我找到一个子串应该如何确定它就是它的异位词呢?
虽然说是可以排序,但是这样做的时间复杂度是\(O(n^{2}\log n)\)
不挣扎了,看人家是怎么做的吧。
学习大神的思路和方法

定长滑动窗口

点击查看代码class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
      vector<int> ans;
      array<int, 26> cnt_p{}; // 统计 p 的每种字母的出现次数
      array<int, 26> cnt_s{}; // 统计 s 的长为 p.length() 的子串 s' 的每种字母的出现次数
      for (char c : p) {
            cnt_p++;
      }
      for (int right = 0; right < s.length(); right++) {
            cnt_s - 'a']++; // 右端点字母进入窗口
            int left = right - p.length() + 1;
            if (left < 0) { // 窗口长度不足 p.length()
                continue;
            }
            if (cnt_s == cnt_p) { // s' 和 p 的每种字母的出现次数都相同
                ans.push_back(left); // s' 左端点下标加入答案
            }
            cnt_s - 'a']--; // 左端点字母离开窗口
      }
      return ans;
    }
};

作者:灵茶山艾府
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/2969498/liang-chong-fang-fa-ding-chang-hua-chuan-14pd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。确实是一样的套路。
有值得我借鉴和学习之处。

[*]array 和 vector 都重载了 == 运算符,可以比较整个数组是否相等;区别在于array是定长的,而vector可以动态向后扩展
分析上述解法的时间复杂度 和 空间复杂度
时间复杂度:\(O(|\sum|m+n)\),\(|\sum|\)是字母表的长度26,m是滑动窗口滑动的次数,n是统计p的迭代次数
继续优化

点击查看代码class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
      vector<int> ans;
      int cnt{}; // 统计 p 的每种字母的出现次数
      for (char c : p) {
            cnt++;
      }
      int left = 0;
      for (int right = 0; right < s.size(); right++) {
            int c = s - 'a';
            cnt--; // 右端点字母进入窗口
            while (cnt < 0) { // 字母 c 太多了
                cnt - 'a']++; // 左端点字母离开窗口
                left++;
            }
            if (right - left + 1 == p.length()) { // s' 和 p 的每种字母的出现次数都相同
                ans.push_back(left); // s' 左端点下标加入答案
            }
      }
      return ans;
    }
};

作者:灵茶山艾府
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/2969498/liang-chong-fang-fa-ding-chang-hua-chuan-14pd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。不定长滑窗可以可以将时间复杂度优化至$O(m+n),贴一下代码吧。
累死了,今天就到这里吧。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 09 滑动窗口:找到字符串中所有字母异位词 438