力扣3-无重复字符的最长子串

bool rep(string str) {
    unordered_set<char> seen;
    for (char c : str) {
        if (seen.find(c) != seen.end()) {
            return true;
        }
        seen.insert(c);
    }
    return false;
}


int lengthOfLongestSubstring(string s) {
    int l=0,r=1;
    int cnt=0;
    string str;
    str=s[0];

    while(r<=s.size()){  
        if(rep(str)){
            l++;
        }else{
            r++;
            cnt=max(static_cast<int>(str.size()),cnt);
        }
        str=s.substr(l,r-l);
        
    }

    return cnt;
}

↑第一个版本,写了一个很傻的

思路是:如果检测到双指针中间夹着的字符串含有重复字符,就将左指针递增;反之则将右指针递增。想了想怎么实现检查重复字符的操作,于是写出了rep函数。

好像有滑动窗口的思想,但是没啥用!如果双重循环暴力截取每一个子串(n^2),并且判断子串是否重复(n),最暴力的写法复杂度是O(n^3)。我除了把双重循环改成单层循环了,但是这样只变成了O(n^2),只能处理几万大小的数据,还是太慢了。

主要是rep那个函数傻,每次都要遍历一遍set。

版本2:

int lengthOfLongestSubstring(string s) {
    unordered_set<char> seen;
    int l = 0, r = 0;
    int max_len = 0;

    while (r < s.size()) {
        if (seen.find(s[r]) == seen.end()) {//没找到,这个字符不是使用过的
            seen.insert(s[r]);
            max_len = max(max_len, r - l + 1);
            r++;
        } else {
            seen.erase(s[l]);
            l++;
        }
    }

    return max_len;
}

不必在每次更新子串后,都从头判断当前子串是不是重复的,因为如果之前都不重复,那新加一个字符的话,只要直接查一下那个字符在不在就行了。版本1我做了好多无效的计算!

相比起1的最大遍历n次判断是否重复,2可以一次查询出该字符串是否为重复子串。把查询操作复杂度降到常数级别,现在的复杂度是O(n)了。

但是还有可以改进的地方:左指针不必每次都递增1,直接跳到重复字符的后面一个就可以。

比如abcb,需要右移两次才能变成cb,中间多判断了一次bcb是否为重复子串,这个也是不必要的。如果我们知道第一个b字符的位置,就可以直接把ab全都删掉。

为了在记录是否重复的情况下记录字符位置,我们可以把unordered_set换成unordered_map。这两者的作用差不多,但是map可以储存key和value,相比起只能储存key的set,正好满足我们的要求。

版本3:

int lengthOfLongestSubstring(string s) {
    unordered_map<char,int> seen;
    int l = 0, r = 0;
    int max_len = 0;

    for (int r = 0; r < s.size(); ++r) {
        if (seen.find(s[r]) != seen.end()) {
           
            l = max(l, seen[s[r]] + 1);
        }
        seen[s[r]] = r; // 更新当前字符的最新位置
        max_len = max(max_len, r - l + 1); // 更新最长无重复子串的长度
    }

    return max_len;
}

l = max(l, char_index[s[r]] + 1),可以保证重复字符一定在框里。

比如abcbda,检测到abcb的时候,会把ab删去变成cb;继续检测到cbda时,能查到a的确已经出现过,但是出现的那段已经被删掉了。

等同于if(当前字符出现过){if(当前字符的下标在当前左指针前)}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值