滑动窗口:LeetCode 3.无重复字符的最长子串

字符串相关的题目在面试中出现的频率是特别高的,主要原因有以下几点:

  • 日常开发过程中字符串是我们打交道相对比较多的一种数据类型,相关题目描述也比较简单易懂,基本不会出现面试官描述完题目候选人不理解题目本身意思的情况。

  • 字符串可玩性高,排序、递归、回溯、二分查找、动态规划等基本都能基于字符串来出题,很适合拿来考察候选人的代码能力。

所以刷题的过程中,字符串相关的题目应该着重去关注。

1. 题目描述

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

输入:一个字符串s。

输出:s无重复字符最长子串的长度。

举例:s = "abcabcbb", 那么其无重复字符的最长子串为"abc", 所以返回值是3。

2. 思路解析

基本上有点编程基础的同学都能想到一种暴力的算法,两个for循环找出所有的子串,针对每个子串去判断是否有重复字符,这样的时间复杂度太高了,很显然这并不是面试官想要的答案。 

下面我来介绍一种基于“滑动窗口”的方式来解此题。所谓滑动窗口,其实就是由两个索引维护一个区间,满足一定条件时会动态去调整这个区间。非常像计算机网络中的tcp协议栈里所用到的滑动窗口算法。这里的两个索引决定了滑动窗口的起点和终点,需要始终保持滑动窗口中的元素是不重复的,这就变成了找到包含非重复元素的最长的一个窗口。

a. i=0, j=0, res=0, u_set={};

b. 字符s[i]不在u_set中那么就把s[i]加入到u_set中,并记录当前窗口的长度,索引i继续向后移动,直到s[i]已经在u_set中,u_set开始删除s[j]并移动索引j,直到s[i]不在u_set中,这时把s[i]加入到u_set中并更新res,res=max(res, cur_window);

c. 向后移动索引i,并重复b中的步骤,直到索引i=s.length。

看到这里有些同学可能还是不太理解,文字描述比较抽象,我特意根据一个简单的例子画了个流程图来帮助理解。理解能力比较强的同学也可以一起感受下我的画功。

比如给定s="abcac",其大致流程如图所示:

从上图中可以看出不含重复字符的窗口[0, 2]、[1, 3]和[3, 4]长度分别为3、3、2,即s无重复字符最长子串的长度为3。

3. C++代码实现

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> u_set;
        int res = 0;
        int len = s.length();
        int cur_window = 0; //当前窗口大小
        for (int i = 0, j = 0; i < len; ++i) {
            while (j < i && u_set.count(s[i])) {
                //s[i]已经在u_set中,窗口区间[j,i]中存在重复的元素
                //移动j直到窗口区间[j,i]中不存在重复元素
                u_set.erase(s[j]); 
                ++j;
                --cur_window;
            } 
            u_set.insert(s[i]);
            ++cur_window;
            res = max(res, cur_window);  
        } 
        return res;
    }
};

4.复杂度分析

时间复杂度:不要看到代码用了一个for嵌套一个while就以为时间复杂度是O(n^2),i,j都是不会回头的两个索引,各自最多只遍历一遍字符串,unordered_set的查找效率一般是O(1),最坏的情况是O(n),这种情况一般是数据量巨大,存在很多哈希冲突,基本上退化成了链表。由于我们字符集大小也就几百个,不会出现这种最坏的情况。故总的时间复杂度为O(n), n为字符串长度。

空间复杂度: 用了三个int型变量,一个set,set的最大元素个数为字符集的个数,所以空间复杂度为3*O(1)+O(m)=O(m), m为字符集的大小。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值