美文网首页
【LeetCode】567. 字符串的排列(滑动窗口)

【LeetCode】567. 字符串的排列(滑动窗口)

作者: 仍有不归期 | 来源:发表于2020-06-16 09:48 被阅读0次

简书内代码已上传GitHub:点击我 去GitHub查看代码

这道题和 最小覆盖子串 思路基本一样

题目:

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。


可以看到, 通过率在 35% 左右, 不算太难。

思路:

先回想一下76题是什么,那题让我们求得最小覆盖子串,而这个子串,其实是覆盖了数组s1的序列。再来看看这一题:

  • s2中只要包含s1的排列,那么返回true, 反之false。这其实就是告诉你,你需要找到一个序列,其中包含且仅有s1的所有元素。

  • 76题我们找的也是一个序列,但是其中不一定只能有s1中的所有元素

  • 这样一看问题就简单了,让我们再对76题做一个简单分析(这很重要,请务必看):


    76题

可以看到, 在76题的算法中,我们的窗口大小一开始是没有限制的,直到找到一个大窗口包含了所需序列,再逐渐缩小。而这一题,我们的窗口大小是固定的,窗口大小就是s1数组的大小。

  • 所以说这一题就相当于76题的简化版,我们不需要考虑窗口大小问题了,我们的目的仅仅是去找这个固定大小窗口在滑动过程中是否匹配到s1的排列。匹配思想还是和76题一样。

  • 如何匹配? 其实记录频率就行,我们关键在于区分一个字符是否是s1中的字符,而区分的方法就是:我们可以用数组存储s1中元素出现的频率。当元素进入窗口,让频率 - 1,如果频率仍然 >= 0,说明这个新入窗的元素是我们需要的元素。同样的出窗操作时频率 + 1。且记录有效元素时进行计数。满足要求时即可返回true。

完整代码:

// 滑动窗口解题
bool checkInclusion(char * s1, char * s2){
    // 长度
    int len1 = strlen(s1), len2 = strlen(s2);
    // 用于计数
    int cnt = len1;
    // 左右指针
    int l, r;
    l = 0; r = 0;
    // 存储s1字母出现频率
    int fre[256] = {0};
    //记录频率
    for(int i = 0; i < len1; ++i){
        ++fre[s1[i]];
    }
    // 遍历开始
    while(r < len2){
        // 新入窗的元素是s1中的元素,计数-1
        if(--fre[s2[r]] >= 0){
            --cnt;
        }
        //滑动窗口
        if(r > len1 - 1){
            //出窗元素是s1中的元素,计数+1
            if(++fre[s2[l++]] > 0){
                ++cnt;
            }
            
        }
        //当前窗口符合要求,返回true
        if(cnt == 0){
            return true;
        }
        //右移窗口
        ++r;
    }
    //执行到这一步,说明不符合,返回false
    return false;
}

在写代码的时候要注意几个问题:

  • 首先如过有循环语句,不要在循环语句的表达式里出现 strlen() ,因为字符串长度是需要O(n)的时间复杂度求得的这样算法的时间复杂度直接就变成O(n*n)。。。

  • 左右指针边界问题,如果让右指针一开始指向窗口右界,那么可能没什么需要注意的。但是如果你让右指针初值为右界 + 1,请注意最后一个窗口的边界问题。。。

  • 这题的判断字符是否在字符串中的思想值得好好的归纳。利用fre与0的比较可以将O(n)时间复杂度的判定问题转换为O(1)。


    AC

接下来还是继续针对滑动窗口做题,希望能够熟练掌握。

如果有看不懂的,私信我!!!
~^^
每天进步一点,加油!

End

END

相关文章

网友评论

      本文标题:【LeetCode】567. 字符串的排列(滑动窗口)

      本文链接:https://www.haomeiwen.com/subject/ndipzhtx.html