Leetcode算法入门第六天(滑动窗口)

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

题目描述

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

样例

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

输入: s = " "
输出: 1

输入: s = ""
输出: 0

思路一

暴力:真就纯暴力破解,两层循环,内层循环是查找从当前字符开始的最长字符串,用哈希表存储,如果发现该字符出现过,更新最大值,并清空哈希表,结束内层循环,不断遍历直到整个字符串都遍历完。因为每次都是遇到出现过的字符才更新最大值,所以最后到结束要再次判断哈希表中元素个数是否超过最大值。

参考代码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int max=0;
        unordered_set<char> hashtable;//存储当前的子串
        //构建哈希表的过程
        for(int i=0;i<s.size();i++)
        {
            //统计从当前字符开始的最长不重复子串,
            for(int j=i;j<s.size();j++)
            {
                //如果当前字符在哈希表中出现过,那么就是前面就是到这里最长的子串了
                if(hashtable.count(s[j]))
                {
                    //判断当前的子串长度有没有比最大值大,有的话更新
                    int count=hashtable.size();
                    if(count>=max) max=count;
                    //清空当前哈希表
                    hashtable.clear();
                    break;
                }
                //把新元素加入子串中:
                //1.这个元素之前出现过的话,已经被清空,也要重新插入
                //2.没有出现过,就插入到前面的字符后面,成为子串的一个。
                hashtable.insert(s[j]);
            }
             //根据最后的哈希表判断需不需要更新最大值:
            int count=hashtable.size();
            if(count>=max) max=count;
        }
       
        return max;
    }
};

思路二

思路一有太多没有必要的重复和循环,时间复杂度太高,比如每次循环都用哈希表重新存储当前字符的最长子串,其实没有必要。我们需要统计的是字符串中的每个字符作为起始位置,不重复能到达的长度,不断更新最大值。
那么可以使用滑动窗口法(即双指针),循环利用存储的哈希表,使用两个指针来表示字符串中某个子串的起点和终点,都是初始化为0(字符串的第一个位置),只需要一次遍历,每一次循环,左指针向右移动一步,表示已经滑动到下一个字符作为起始位置。 这时可以删除前面一个存在哈希表的元素,因为它现在已经不在窗口中了,达到循环利用哈希表的目的。右指针也是不断向前,插入它所指的当前元素进哈希表,直到遇到哈希表中出现过的元素,去更新最大值right-left与max中的最大值。

补充:

滑动窗口
什么是滑动窗口呢?若两个指针指向同一数组,遍历方向相同且不会相交,则称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。

参考代码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int max=0;
        unordered_set<char> hashtable;//存储当前的子串
        int right=0;
        //构建哈希表的过程
        for(int left=0;left<s.size();left++)
        {
            //当前滑动到下一个字符,需要删除哈希表中前一个元素
            //左指针向右移动一格,就删除哈希表中一个字符
            if(left!=0) 
            {
                hashtable.erase(s[left-1]);
            }
            //开始统计不重复子串长度
            while(right<s.size()&&!hashtable.count(s[right]))
            {
                hashtable.insert(s[right]);
                //不断移动右指针
                right++;
            }
            //现在更新最长不重复子串
            if(right-left>max) max=right-left;
        }
        
        return max;
    }
};

567. 字符串的排列

题目描述

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

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

样例

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

思路

因为题目要求的是判断字符串s1的排列是不是s2的子串。因为排列不计较顺序,只要是s2的子串中长度和出现的字符种类以及个数等于s1的就可以了。所以首先统计字符串s1出现的每种字符次数,存在cnt1数组中。然后每次通过指针移动,分别统计s2中长度为n的子串出现的每种字符次数,存储在cnt2数组中,直到指针走到s2的末尾,如果两数组相等,说明满足条件直接返回。其中,每次指针向右移动一次,为了保证区间长度为n,当指针超过n时,必须剔除n个元素前面的一个。

参考代码

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        int n=s1.size();//字符串s1长度
        int m=s2.size();
        if(n>m) return false;
        //统计s1,s2中所有出现过的字符个数。
        vector<int> cnt1(26,0);
        vector<int> cnt2(26,0);
        //首先统计s1每个字符出现的次数
        for(int i=0;i<n;i++)
        {
            cnt1[s1[i]-'a']++;
        }
       ;
        //每次向右移动一位,保证区间长度为n,判断两个数组是否相等
        for(int i=0;i<m;i++)
        {
            //把最新一位字符统计进数组
            cnt2[s2[i]-'a']++;
            //删除不在窗口内的元素
            if(i>=n) cnt2[s2[i-n]-'a']--;
            
            if(cnt1==cnt2) return true;
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值