算法/滑动窗口

滑动窗口分为不固定窗口大小和固定窗口大小两种:(当要寻找符合某个要求的子串时常用

1)不固定窗口大小:窗口大小会变化,当前窗口不满足要求时,整体向后移。

2)   固定窗口大小:窗口大小为一个常数,当前窗口不满足要求时,整体向后移。

滑动窗口与双指针不同的地方就在于,双指针的变化方向是双向的,可以左指针向右,也可以右指针向左,而滑动窗口是整个窗口向右移,左右指针同步。

1.3. 无重复字符的最长子串

题目描述:

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例:

示例 1:
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

解答描述:

要找到不含有重复字符的最长子串的长度,采用滑动窗口的思想。

枚举每个字符作为字符串的起点,找到以该字符作为起点的没有重复的最长字符串,当有重复时,立即停止,然后滑动窗口的左指针向右移,继续枚举下一个字符串。

因为滑动窗口内的字符一定是没有重复的,所以只需要判断右窗口之外的字符即可。

(注意,在C++中由于STL有容器,可以采用unordered_set来进行查重,当set.count==0的时候表示该元素没有重复,set.count==1的时候,表示容器内已经有该元素,会造成重复)

代码:

C++滑动窗口:

class Solution {
public:
    int max(int a,int b)
    {
        return a>b?a:b;
    }
    int lengthOfLongestSubstring(string s) {
        int l=0;//用来标志枚举字符串的起始位置
        int r=0;//用来找到当前枚举字符串的终止位置
        unordered_set<char> ss;//用来去重
        int n=s.size();
        int max_str_len=0;
        int temp_len=0;//用来记录当前枚举字符串的最长子串长度
        while(l<n)
        {  
            while(r<n && ss.count(s[r])==0)
            {
                ss.insert(s[r]);
                r++;
                temp_len++;
            }
            max_str_len=max(max_str_len,temp_len);  
            ss.erase(s[l]);
            temp_len--;
            l++;
            
        }
        return max_str_len; 
    }
};

C kmp算法:

//kmp算法
int max(int a,int b)
{
    return a>b?a:b;
}
int lengthOfLongestSubstring(char * s){
    int size=strlen(s);
    int max_len=0;
    int *next=malloc(sizeof(int)*256);
    //int next[256]={0};//用来记录每一轮各数出现的次数
    if(size<2)//当为空字符串时,直接返回0,当字符串只有一个字符时,返回1
    {
        return size;
    }
    memset(next,0,sizeof(next));        
    int j=0;
    for(int i=0;i<size;i++)
    {
        j=max(next[s[i]],j);
        max_len=max(max_len,i-j+1);
        next[s[i]]=i+1;   
    }
    return max_len;
};

2.567. 字符串的排列

题目描述:

给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。

换句话说,s1 的排列之一是 s2 的 子串 。

示例:

示例 1:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").

示例 2:
输入:s1= "ab" s2 = "eidboaoo"
输出:false

解答描述:

这道题的关键在于理解s2包含s1的排列,这意味着s2只要有一个子串和s1的元素种类和数量都相同即可。所以考虑用一个固定大小为s1.size()的滑动窗口来解决。

先用数组cnt1,cnt2分别记录s1的各个元素出现次数和s2当前窗口内的元素出现次数,如果cnt1和cnt2完全相同,说明匹配,返回true,否则的话,将滑动窗口整体向后移一步,更新cnt2,继续判断。

代码:

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int n1=s1.size();
        int n2=s2.size();
        if(n1>n2)
        {
            return false;
        }
        vector<int> cnt1(26);//用来统计固定大小为n1的滑动窗口内s1各字符出现次数
        vector<int> cnt2(26);//用来统计固定大小为n1的滑动窗口内s2各字符出现次数
        for(int i=0;i<n1;i++)
        {
            cnt1[s1[i]-'a']++; 
            cnt2[s2[i]-'a']++;
        }
        if(cnt1==cnt2)
        {
            return true;
        }
        int l=0;
        int r=n1;
        while(r<n2)
        {
            cnt2[s2[l]-'a']--;
            cnt2[s2[r]-'a']++;
            if(cnt1==cnt2)
            {
                return true;
            }
            l++;
            r++;
        }
        return false;
    }
};

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值