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

题目

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

示例 1:

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

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

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解答一

设置变量来保存已知最长的子串长度,以及当期的子串长度。
使用数组来保存当期的子字符串。
当遇到重复的字符时,先计算数组当期字符串的长度,看是否比最长的要长,是则更新最长长度。
接着从数组中重复的字符开始截取子串,继续追加字符,直到下一次再遇到重复字符。
需要注意的是:不要漏掉最后一个子串。

class Solution {

    /**
     * @param String $s
     * @return Integer
     */
    function lengthOfLongestSubstring($s) {
        $maxLen = 0;
        $currLen = 0;
        $length = strlen($s);
        $currArr = [];

        for ($i = 0; $i < $length; $i++) {
            $index = array_search($s[$i], $currArr);
            if ($index === false) {
                array_push($currArr, $s[$i]);
            } else {
                $currLen = count($currArr);
                $currLen > $maxLen and $maxLen = $currLen;

                array_splice($currArr, 0, $index+1);
                array_push($currArr, $s[$i]);
            }
        }

        $currLen = count($currArr);
        $currLen > $maxLen and $maxLen = $currLen;

        return $maxLen;
    }
}

执行用时:20 ms, 在所有 PHP 提交中击败了62.69%的用户

内存消耗:19 MB, 在所有 PHP 提交中击败了21.03%的用户

通过测试用例:987 / 987

解答二

更换直接使用子字符串的数据格式来替代数组保存子串。
相比于数组操作,字符串的操作效率更高,所以速度提升明显。

class Solution {

    /**
     * @param String $s
     * @return Integer
     */
    function lengthOfLongestSubstring($s) {
        $maxLen = 0;
        $currLen = 0;
        $length = strlen($s);
        $currStr = '';

        for ($i = 0; $i < $length; $i++) {
            $index = strpos($currStr, $s[$i]);
            if ($index === false) {
                $currStr .= $s[$i];
            } else {
                $currLen = strLen($currStr);
                $currLen > $maxLen and $maxLen = $currLen;

                $currStr = $index+1 == $currLen ? '' : substr($currStr, $index+1);
                $currStr .= $s[$i];
            }
        }

        $currLen = strLen($currStr);
        $currLen > $maxLen and $maxLen = $currLen;

        return $maxLen;
    }
}

执行用时:12 ms, 在所有 PHP 提交中击败了92.31%的用户

内存消耗:19.1 MB, 在所有 PHP 提交中击败了15.64%的用户

通过测试用例:987 / 987

解答三

思考不用新建一个字符串的方式去存储零时子串,而是用一个头索引标记当前子串即可,这样内存使用肯定会下来,速度不好说。

class Solution {

    /**
     * @param String $s
     * @return Integer
     */
    function lengthOfLongestSubstring($s) {
        $maxLen = 0;
        $currLen = 0;
        $currStart = 0;
        $length = strlen($s);

        for ($i = 0; $i < $length; $i++) {
            for ($j = $currStart; $j < $currStart + $currLen; $j++) {
                if ($s[$i] === $s[$j]) {
                    $currLen > $maxLen and $maxLen = $currLen;
                    $currStart = $j + 1;
                    $currLen = $i - $j;
                    continue 2;
                }
            }
            $currLen++;
        }

        return $currLen > $maxLen ? $currLen : $maxLen;
    }
}

执行用时:12 ms, 在所有 PHP 提交中击败了92.31%的用户

内存消耗:18.6 MB, 在所有 PHP 提交中击败了60.77%的用户

通过测试用例:987 / 987

解答四 ✅

尝试使用Array中的hashmap功能是记录是有已存在字符。
并且Array中的映射不清除,这样虽然内存会多占用一点,但是性能会更高。

class Solution {

    /**
     * @param String $s
     * @return Integer
     */
    function lengthOfLongestSubstring($s) {
        $max = $start = 0;
        $total = strlen($s);
        $arr = [];

        for ($i = 0; $i < $total; $i++) {
            if (isset($arr[$s[$i]]) && $arr[$s[$i]] >= $start) {
                $i - $start > $max and $max = $i - $start;
                $start = $arr[$s[$i]] + 1;
            }
            $arr[$s[$i]] = $i;
        }

        return $i - $start > $max ? $i - $start : $max;
    }
}

执行用时:8 ms, 在所有 PHP 提交中击败了98.21%的用户

内存消耗:18.9 MB, 在所有 PHP 提交中击败了23.08%的用户

通过测试用例:987 / 987

对比别人写的C++解答

感觉C++还是要快一些的。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        int maxlen = 0;
        vector<int> exist(130,0);
        int i=0;
        for(int j=0; j<n; ++j){
            exist[s[j]]++;
            while(exist[s[j]]>=2) exist[s[i++]]--;
            maxlen=max(maxlen,j-i+1);
        }
        return maxlen;
    }
};

执行用时:8 ms, 在所有 C++ 提交中击败了86.52%的用户

内存消耗:7.7 MB, 在所有 C++ 提交中击败了76.77%的用户

通过测试用例:987 / 987

对比别人java的解答

java感觉好像开挂了,只用了4 ms,但是内存占用多了很多。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int maxLength = 0;
        Queue<Character> queue = new LinkedList<>();
        for (int i = 0; i < s.length(); i++) {
            if (queue.contains(s.charAt(i))){
                maxLength = Math.max(maxLength, queue.size());
                while (queue.poll() != s.charAt(i));
            }
            queue.offer(s.charAt(i));
        }
        return Math.max(maxLength, queue.size());
    }
}

执行用时:4 ms, 在所有 Java 提交中击败了86.45%的用户

内存消耗:41.3 MB, 在所有 Java 提交中击败了64.42%的用户

通过测试用例:987 / 987

本作品采用《CC 协议》,转载必须注明作者和本文链接
xiaer
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!