LeetCode | Longest Substring Without Repeating Characters(最长连续不重复子串)


题目:

Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for “abcabcbb” is “abc”, which the length is 3. For “bbbbb” the longest substring is “b”, with the length of 1.

找出最长不重复的连续子串。


题目解析:

题意很简单,就是要找到最长的连续的子串。


思路一:

最先考虑到的就是求第i位字符开头的,最大的子串长度。没增加第j位的字符,就要与i — j-1之间的字符相比较,直到出现重复或者遍历完为止。这也是最复杂的,需要O(n^2)时间复杂度。中间要设置一个maxlen变量,判断第i位开始的字符是否超过了最大长度。


思路二:

我们可以从《最大连续子数组和》得到启发,我们求第i位字符结尾的最大长度:遍历到第i位的时候,如果和前面不重复,就max++(这里为max不是maxlen);如果重复,通过遍历从i-maxlen到i-1位的字符,与第i位字符相比较,找到相等的位j,那么maxlen可能会被i-j更新!

然后再遍历i+1位的字符。。。

这个思路比上面的有一点改进,不过也是需要比较后才能得到,时间复杂度也有一点高。


思路三:

通常我们要降低时间复杂度,可以考虑牺牲空间来获得。

我们通过hash表,并附设两个“指针”i和j;用字符的整数值当索引,值为0或1表示是否之前存在。当存在了,就将i++并清零,直到j所表示的字符被清零为止。

比如abcdba,当j等于4的时候,arr[1]是为1的,此时i为0,那么先将arr[0]和arr[1]清零,然后再对arr[1]赋值为1。

int LengthOfLongestSubstring(char *str)
{
    int i,j;
    int maxlen = 0; //要初始化,不然产生意外结果!
    int n = strlen(str);
    int arr[256] = {0};     //设置hash表,索引为字符的值,值为是否存在该字符!

    for(i = 0,j = 0;j < n;j++){
        while(arr[str[j]] == 1){
            arr[str[i]] = 0;
            ++i;
        }
        arr[str[j]] = 1;
        maxlen = maxlen > (j-i) ? maxlen : j-i+1;
    }

    return maxlen;
}

思路四:

通常我们能想到用哈希表,但是往往在索引和值表示的概念上造成混乱。一般索引用要判断字符是否存在的字符值来表示,而值就是我们关心的是否存在,这就得出了思路三。但进一步想想,我们知道了他存在,但还想知道具体位置。

方案一:我们可以再另外附设一个数组,来表示其存储的位置

用一个start和end来记录目前考察的字符串的开头和结尾
用exist[26]来记录目前字符串中出现过的字母
用position[26]来记录出现过的字符串的字母的位置

然后我们往后走一位,然后和exist来比较看这个字母是否已经出现过了。

1 如果出现过了,那么我们把start移动到之前那个字母出现的位置的后一位,end往后移动一位
2 如果没有出现过,那么我们就把end往后移动一位

代码:

int lengthOfLongestSubstring(string s) 
{
    int max = 0, start = 0;
    bool exist[26];
    int position[26];
    
    for(int i = 0; i < 26; i++) {
        exist[i] = false;
        position[i] = 0;
    }
    
    for(int i = 0; i < s.size(); i++) {
        if(exist[s[i] - 'a']) {
            for(int j = start; j <= position[s[i] - 'a']; j++) {
                exist[s[j] - 'a'] = false;
            }
            start = position[s[i] - 'a'] + 1;
            exist[s[i] - 'a'] = true;
            position[s[i] - 'a'] = i;
        }
        else {
            exist[s[i] - 'a'] = true;
            position[s[i] - 'a'] = i;
            max = max > (i - start + 1) ? max : (i - start + 1);
        }
    }

    return max;
}

方案二:

—–错误的做法!!!比如abbbbbba应该输出2,但是按照这个的话,会输出7。

我们不仅要保存位置,还要取 len = min(len+1,j-index); if(len > max) max = len。之所以要取最小值,是因为len的长度要么大幅度减小,要么一个一个增加,而j-index可能很大。


我们完全可以让思路三中的arr[]数组存储位置即可!当存在了,就更新其为最新的位置即可。当已经存在了,就将j-index与最大长度相比,看是否更新,并将索引数组更新为j。

代码如下:

int LengthOfLongestSubstring1(char *str)
{
    int idex = -1,j;
    int maxlen = 0; //要初始化,不然产生意外结果!
    int n = strlen(str);
    int arr[256];     //设置hash表,索引为字符的值,值为是否存在该字符!

    memset(arr,-1,256 * sizeof(int));   //设置的是字节数,因此要乘以sizeof(int)
    for(j = 0;j < n;j++){
        if(arr[str[j]] > -1){
            idex = arr[str[j]];
        }
        arr[str[j]] = j;
        if(j-idex > maxlen)
            maxlen = j-idex;
    }

    return maxlen;
}

正确代码:

class Solution {
public:
    int lengthOfLongestSubstring(string s){
        int idex = -1,j;
        int maxlen = 0,len = 0; //要初始化,不然产生意外结果!
        int n = s.size();
        if(n == 0)
            return 0;
        int arr[256];     //设置hash表,索引为字符的值,值为是否存在该字符!
    
        memset(arr,-1,256 * sizeof(int));   //设置的是字节数,因此要乘以sizeof(int)
        for(j = 0;j < n;j++){
            idex = arr[s[j]];
            arr[s[j]] = j;
            len = len+1 > j-idex ? j-idex : len+1;
            if(len > maxlen)
                maxlen = len;
        }

        return maxlen;
    }
};

点赞