Longest Substring Without Repeating Characters

一. Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.
Given “bbbbb”, the answer is “b”, with the length of 1.
Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

Difficulty:Medium

解法

首先想到的当然是用哈希表保存出现的字符,并记录该字符在字符串中的位置。循环的时候,如果发现重复的字符,就记录下哈希表中所有字符的数目,并清空哈希表,然后将循环的索引定位为哈希表中保存的重复字符位置加一(因为重复字符之前的字符串可以直接舍弃),继续循环,代码如下:

int lengthOfLongestSubstring(string s) {
    unordered_map<char, int> info;
    int len = 0;
    for (int i = 0; i < s.size(); i++) {
        if (info.find(s[i]) == info.end()) {
            info[s[i]] = i;
        }
        else {
            i = info[s[i]];
            if (info.size() > len)
                len = info.size();
            info.clear();
        }
    }
    if (info.size() > len)
        len = info.size();
    return len;
}

代码的时间复杂度介于 O(n) O(n2) 之间。

优化

上面的代码还有很多可以优化的地方:

  • 首先我们知道map使用红黑树实现的,查询复杂度为 O(log2n) ,而unordered_map是用哈希表实现的,查询复杂度接近于 O(1) 。再看这道题,哈希键的范围是确定的,而且字符是可以隐式转换为数字的,因此可以直接使用vector或者是数组来实现哈希,那么查询复杂度就完全等于 O(1)
  • 另外一点就是程序中重置了数组的索引,这并不是必要的,我们可以采用双索引的机制,另一个索引,假如命名为begin,指向不重复串起始的位置,而串的长度就是循环的索引减去begin的值。
  • 有了双索引之后,我们就可以不必清空哈希表了(之所以清空哈希表是因为担心无法判断之前截断字符串中重复的字符),因为如果新字符的位置比begin小的话,说明这个字符要么没有出现过,要么是之前截断字符串中的字符,可以加入当前不重复子串中。
int lengthOfLongestSubstring(string s) {
    vector<int> info(256, -1); // 字符数目的ASCII码值不会大于256
    int len = 0, begin = -1; //begin要从-1开始,就是指向不重复子串开始位置的前一个位置
    for (int i = 0; i < s.size(); i++) {
        if (info[s[i]] > begin)
            begin = info[s[i]];
        info[s[i]] = i;
        if (i - begin > len)
            len = i - begin;

    }
    return len;
}

代码的时间复杂度为 O(n)

总结

字符在字符串中的位置往往能提供很多额外的信息,比如这里的可以通过位置区分该字符是和截断的字符串中的字符重复还是和不重复子串中的字符重复,从而降低了代码的复杂度。但是如果不是因为双索引,也不是特别容易往这个方向想。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值