leetcode-pp / 91alg-6-daily-check

91 算法第六期打卡仓库
28 stars 0 forks source link

【Day 22 】2022-01-02 - 3. 无重复字符的最长子串 #29

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

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

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

前置知识

示例 1:

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

示例 2:

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

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

Riuusee commented 2 years ago

思路

滑动窗口(还不是特别会)

代码

import java.util.HashMap;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap<Integer,Integer> map = new HashMap<>();
        int left = 0;
        int right = 0;
        int m = s.length();
        int ans = 0;
        while (right < m)
        {
            int t = s.charAt(right) - 'a';
            while(map.getOrDefault(t,0) > 0 && left < right)
            {
                int t2 = s.charAt(left) - 'a';
                map.put(t2, map.getOrDefault(t2, 0) - 1);
                left += 1;

            }
            map.put(t, map.getOrDefault(t,0) + 1);
            right += 1;
            if (right-left > ans)
            {
                ans = right - left;
            }
        }
        return ans;
    }
}

复杂度分析

RMadridXDH commented 2 years ago

代码

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 哈希集合,记录每个字符是否出现过
        occ = set()
        n = len(s)
        # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        rk, ans = -1, 0
        for i in range(n):
            if i != 0:
                # 左指针向右移动一格,移除一个字符
                occ.remove(s[i - 1])
            while rk + 1 < n and s[rk + 1] not in occ:
                # 不断地移动右指针
                occ.add(s[rk + 1])
                rk += 1
            # 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1)
        return ans
Today-fine commented 2 years ago

学习官方题解


class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 哈希集合,记录每个字符是否出现过
        unordered_set<char> occ;
        int n = s.size();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1, ans = 0;
        // 枚举左指针的位置,初始值隐性地表示为 -1
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.erase(s[i - 1]);
            }
            while (rk + 1 < n && !occ.count(s[rk + 1])) {
                // 不断地移动右指针
                occ.insert(s[rk + 1]);
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1);
        }
        return ans;
    }
};
guoling0019 commented 2 years ago

代码

JavaScript Code:


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    const occ = new Set();
    const n = s.length;
    let rk = -1, ans = 0;
    for (let i = 0; i < n; ++i) {
        if (i != 0) {
            occ.delete(s.charAt(i - 1));
        }
        while (rk + 1 < n && !occ.has(s.charAt(rk + 1))) {
            occ.add(s.charAt(rk + 1));
            ++rk;
        }
        ans = Math.max(ans, rk - i + 1);
    }
    return ans;
};

复杂度分析

令 n 为数组长度。

Bong6 commented 2 years ago
public int lengthOfLongestSubstring(String s) {
    if (s == null){
        return 0;
    }

    HashMap<Character,Integer> map = new HashMap<>();
    int length = s.length();
    int res = 0;

    for (int start = 0, end = 0; end < length; end++) {
        char c = s.charAt(end);
        if (map.containsKey(c)){
            start = Math.max(map.get(c)+1,start);
        }
        res = Math.max(res,end - start + 1);
        map.put(c,end);
    }
    return res;
}
mannnn6 commented 2 years ago
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> occ = new HashSet<Character>();
        int n = s.length();
        int rk = -1, ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                occ.remove(s.charAt(i - 1));
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
                occ.add(s.charAt(rk + 1));
                ++rk;
            }
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;
    }
}
moirobinzhang commented 2 years ago

Idea: Two Pointers with HashSet

Code: public class Solution { public int LengthOfLongestSubstring(string s) {

    HashSet<char> strSet = new HashSet<char>();
    int left = 0;
    int right = 0;
    int maxLen = 0;

    while(right < s.Length)
    {
        while(strSet.Contains(s[right]))
        {
            strSet.Remove(s[left++]);
        }

        strSet.Add(s[right]);
        maxLen = Math.Max(maxLen, right - left + 1);
        right++;            
    }

    return maxLen;
}

}

LiFuQuan1208 commented 2 years ago

思路:

每天靠标签知道用什么方法,自己还是判断不出来,今天是学习滑动窗口的一天

代码:

public int lengthOfLongestSubstring(String s) {
       if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i ++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(left,map.get(s.charAt(i)) + 1);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-left+1);
        }
        return max;
}

复杂度分析 -时间复杂度:O(N) -空间复杂度:O(N)

Jay214 commented 2 years ago

思路

之前已经有做过,再重新做一遍,结合双指针和hash表,从头开始循环,维护一个不重复的子字符串区间,left表示区间起点,用map存储遍历过的字符,当当前字符存在于map并且对应索引大于等于left时表示该字符已在区间中,更新left为重复字符位置+1,否则计算max = Math.max(max, i - left + 1)

代码

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    if (s.length < 2) {
        return s.length;
    }

    let left = 0
    let max = 1
    const map = new Map()
    for (let i = 0; i < s.length; i++) {
        if (map.has(s[i]) && map.get(s[i]) >= left) {
            left = map.get(s[i]) + 1
        } else {
            max = Math.max(max, i - left + 1)
        }
        map.set(s[i], i)
    }
    return max
}

复杂度分析

空间O(N),时间O(N),N为字符串长度

zzzpppy commented 2 years ago

思路 滑动窗口,遇到不同字符窗口扩大,使用map存储字符以及结果,最后不断更新返回结果 代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int length = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < length; end++) {
            char c = s.charAt(end);
            if (map.containsKey(c)) {
                start = Math.max(map.get(c), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;
    }
}

复杂度 时间复杂度O(N) 空间复杂度O(N)

xianxianxianyu commented 2 years ago

还就那个经典

class Solution {
public:
      int lengthOfLongestSubstring(string s) {
        vector<int> m(128, 0);
        int ans = 0;
        int i = 0;
        for (int j = 0; j < s.size(); j++) {
            i = max(i, m[s[j]]);
            m[s[j]] = j + 1;
            ans = max(ans, j - i + 1);
        }
        return ans;
    }
};
alongchong commented 2 years ago
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> occ = new HashSet<Character>();
        int n = s.length();
        int rk = -1, ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                occ.remove(s.charAt(i - 1));
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
                occ.add(s.charAt(rk + 1));
                ++rk;
            }
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;
    }
}
Alfie100 commented 2 years ago

解题思路

  1. 动态规划+哈希表。
  2. 双指针+哈希表。

1. 动态规划+哈希表

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:

        if not s:
            return 0

        n = len(s)
        dic = {}        # 哈希表记录每个字符(上一次)出现的位置
        dic[s[0]] = 0
        dp = [1] * n    # dp[i],以s[i]结尾的最长无重复字符串

        for j, ch in enumerate(s[1:], start=1):
            i = dic.get(ch, -1)     # ch上次出现的位置
            dic[ch] = j             # 更新ch出现的位置
            dp[j] = min(dp[j-1] + 1, j-i)      # 状态转移方程

        return max(dp)

2. 双指针+哈希表

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:

        if not s:
            return 0

        ans = 0
        dic = {}        # 哈希表记录每个字符(上一次)出现的位置
        left = -1       # 无重复字符串的左边界

        for right, ch in enumerate(s):
            # i = dic.get(ch, -1)     # ch上次出现的位置;默认为-1,即ch未出现过
            # left = max(i, left)     # 更新左边界
            if ch in dic:           # ch出现过
                left = max(dic[ch], left)     # 更新左边界
            dic[ch] = right         # 更新ch出现的位置
            ans = max(ans, right-left)

        return ans

复杂度分析

Okkband commented 2 years ago
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.size();
        int ret = 0;
        int right = -1;
        unordered_set<char> mp;
        for(int i=0; i<n; i++){
            if (i != 0){
                mp.erase(s[i-1]);
            }
            while(right + 1 < n && !mp.count(s[right+1])){
                mp.insert(s[right+1]);
                right++;
            }
            ret = max(ret, right - i + 1);
        }
        return ret;
    }
};
fornobugworld commented 2 years ago

思路:滑动窗口+哈希表 时间复杂度O(nk) class Solution: def lengthOfLongestSubstring(self, s: str) -> int: if s=="": return 0 ans =1 for i in range(len(s)-1): dic = {s[i]:1} leng = 1 for j in range(i+1,len(s)): if s[j] in dic: break else: leng +=1 dic[s[j]]=1 ans = max(ans,leng) return ans

Yachen-Guo commented 2 years ago

思路

滑动窗口

代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();
        for (int end = 0, start = 0; end < n; end++) {
            char alpha = s.charAt(end);
            if (map.containsKey(alpha)) {
                start = Math.max(map.get(alpha), start);
            }
            ans = Math.max(ans, end - start + 1);
            map.put(s.charAt(end), end + 1);
        }
        return ans;
    }
}
frgthyju commented 2 years ago

动态规划,dp[i]记录到该字符到最大不重复字符数,用哈希表记录字符大小。

func maxNum(a,b int)int{
    if a>b{
        return a
    }
    return b
}
func minNum(a,b int)int{
    if a>b{
        return b
    }
    return a
}
func lengthOfLongestSubstring(s string) int {
    m := map[byte]int{}
    dp := make([]int,len(s)+1)
    max := 0
    for i:=1;i<=len(s);i++{
        if v,ok := m[s[i-1]];ok{
            dp[i] = minNum(i-v,dp[i-1]+1)
        }else{
            dp[i] = dp[i-1]+1
        }
        m[s[i-1]]=i
        max = maxNum(max,dp[i])
    }
    return max
}

时间复杂度O(n)

空间复杂度O(n+m),n为dp的存储空间,m为哈希表中字符集大小

xinhaoyi commented 2 years ago

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

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

思路一:双指针+哈希表

代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //双指针 + 哈希表
        //左闭右开
        int left = 0;
        int right = 0;
        int length = s.length();
        //长度最大值
        int max = 0;
        Map<Integer,Integer> map = new HashMap<>();
        while(right < length && left <= right){
            char newCharacter = s.charAt(right);
            //重复了
            if(map.containsKey(newCharacter - 'a')){
                //更新max
                max = Math.max(max, right - left);
                int repeatIndex = map.get(newCharacter - 'a');
                //把map中当前left到repeatIndex的元素全部删掉
                //原来说:不要删repeatIndex,因为新加入的也是这个字母,这是错误的!因为你的value也就是坐标值是变化了的,必须要删掉重新更新
                for(int i = left; i <= repeatIndex; i++){
                    map.remove(s.charAt(i) - 'a');
                }
                //left指向原来的重复元素的后一个元素的下标
                left = repeatIndex + 1;
            }

            //把新节点加进map中,value值为该节点的坐标
            map.put(newCharacter - 'a', right);
            right ++;
        }
        return Math.max(max, right - left);
    }
}

复杂度分析

时间复杂度:$O(n)$

空间复杂度:$O(n)$

bluetomlee commented 2 years ago

思路

hash记录索引是否出现过,然后while遍历

代码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 哈希集合,记录每个字符是否出现过
        unordered_set<char> occ;
        int n = s.size();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1, ans = 0;
        // 枚举左指针的位置,初始值隐性地表示为 -1
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.erase(s[i - 1]);
            }
            while (rk + 1 < n && !occ.count(s[rk + 1])) {
                // 不断地移动右指针
                occ.insert(s[rk + 1]);
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1);
        }
        return ans;
    }
};
for123s commented 2 years ago

代码

C++ Code:


class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.size()==0)
            return 0;
        unordered_map<char,int> mp;
        int l = 0, r = 1;
        int ret = 1;
        mp[s[0]] = 0;
        while(r<s.size())
        {
            if(mp.count(s[r])==0||mp[s[r]]==-1)
            {
                mp[s[r]] = r;
                ret = max(ret, r+1-l);
            }
            else
            {
                while(s[l]!=s[r])
                    mp[s[l++]] = -1;
                l++;
                mp[s[r]] = r;
            }
            ++r;
        }
        return ret;
    }
};
yetfan commented 2 years ago

思路 遍历右边 直到找到第一个重复。停止 记录长度 左边上到前方重复字符右边一个的位置 用来避免重复 直到右边抵达边框,看看最后长度,然后返回最长的长度

代码

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        left = 0
        res = 0
        for i, ch in enumerate(s):
            if ch in s[left:i]:
                res = max(res, i-1-left+1)
                left = left + s[left:i].index(ch) + 1
        res = max(res, len(s)-1-left+1)

        return res

复杂度 时间 O(n) 空间 O(1)

CodeFarmersWorld commented 2 years ago

class Solution { public int lengthOfLongestSubstring(String s) { Set occ = new HashSet(); int n = s.length(); int rk = -1, ans = 0; for (int i = 0; i < n; ++i) { if (i != 0) { occ.remove(s.charAt(i - 1)); } while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) { occ.add(s.charAt(rk + 1)); ++rk; } ans = Math.max(ans, rk - i + 1); } return ans; } }

ozhfo commented 2 years ago

思路

代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 无重复、最长子串、的长度
        Set<Character> charCount = new HashSet<>();
        char[] strArray = s.toCharArray();
        int maxCount = 0;
        int l = 0;
        for (int i = 0; i < strArray.length; i++) {
            if (charCount.contains(strArray[i])) {
                // 只要出现重复 删除元素直至 set不包含即将添加的元素
                while (l < i && charCount.contains(strArray[i])) {
                    charCount.remove(strArray[l]);
                    l++;
                }
            }
            // 加入新元素 更新max
            charCount.add(strArray[i]);
            maxCount = Math.max(charCount.size(), maxCount);
        }
        return maxCount;
    }
}

复杂度

niuyibo0619 commented 2 years ago
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if s=='':
            return 0
        ml=1
        for i in range(len(s)):
            e=[]
            e.append(s[i])
            ans=1
            for j in range(i+1,len(s)):
                if s[j] in e:

                    break
                e.append(s[j])
                ans+=1
            if ans>ml:
                ml=ans

        return ml
linrAx commented 2 years ago

代码

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i ++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(left,map.get(s.charAt(i)) + 1);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-left+1);
        }
        return max;

    }
}

LinnSky commented 2 years ago

思路

map过滤重复元素,并且更新最长子串

代码

    const set = new Set()
    let i = 0, j = 0, maxLength = 0
    if(s.length === 0) return 0
    for(i; i < s.length; i++) {
        if(!set.has(s[i])) {
            set.add(s[i])
            maxLength = Math.max(maxLength, set.size)
        } else {
            while(set.has(s[i])) {
                set.delete(s[j])
                j++
            }
            set.add(s[i])
        }     
    }
    return maxLength

复杂度分析

RocJeMaintiendrai commented 2 years ago
class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null || s.length() == 0) return 0;
        int res = 0;
        Map<Character, Integer> map = new HashMap<>();
        for(int i = 0, j = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if(map.containsKey(c)) {
                j = Math.max(j, map.get(c) + 1);
            }
            map.put(c, i);
            res = Math.max(res, i - j + 1);
        }
        return res;
    }
}

复杂度分析

caohaha commented 2 years ago

思路

滑动窗口

python code

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if (len(s) == 0) or (len(s) == 1):
            return len(s)
        # 滑动窗口的左右边界
        l,r = 0,0
        # 结果
        ret = 0
        # 保存最后一次出现的位置
        char_dict = {}
        char_dict[s[0]] = 0
        for r in range(1,len(s)):
            if (s[r] in char_dict) and char_dict[s[r]] >= l:
                l = char_dict[s[r]] + 1
            char_dict[s[r]] = r
            ret = max(ret,r-l+1)
        return ret
linyang4 commented 2 years ago

思路

滑动窗口 维护2个索引, left和right, 用来控制窗口的边界; 创建一个set用于存储窗口里的值, 先移动right索引, 然后取字符串对应索引的值.

代码

var lengthOfLongestSubstring = function(s) {
    let ans = 0
    let left = 0
    let right = 0
    const charSet = new Set()
    while(right < s.length) {
        const rightChar = s[right]
        while(charSet.has(rightChar)) {
            const leftChar = s[left]
            left++
            charSet.delete(leftChar)
        }
        charSet.add(rightChar)
        right++
        ans = Math.max(ans, charSet.size)
    }
    return ans
};

复杂度

iambigchen commented 2 years ago

思路

暴力解,如果遇到重复值,则把索引值向前回归,回到重复值的下一位。

关键点

代码

JavaScript Code:


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let str = ''
    let max = 0
    for(let i = 0; i < s.length; i++) {
        if (str.indexOf(s[i]) === -1) {
            str = str + s[i]
            max = Math.max(str.length, max)
        } else {
            str = ''
            let index = i - 1
            while(s[i] !== s[index]) {
                index--
            }
            i = index
        }
    }
    return max
};

复杂度分析

令 n 为数组长度。

思路2

滑动窗口 + hashmap

左右指针,用hashmap存储每个字符串出现过的位置,如果右指针指向的值,在hashmap中的坐标,是在左右指针中间,则需要更改窗口大小。将左指针指向该坐标+1.更新hashmap中该字符的坐标。

关键点

代码

JavaScript Code:


/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let left = (right = 0)
    let max = 0
    let map = {}
    while(right < s.length) {
        const pos = map[s[right]];
        if (pos >= left && pos <= right) {
            left = pos + 1
        }
        map[s[right]] = right
        max = Math.max(max, right - left + 1)
        right++
    }
    return max
};

复杂度分析

令 n 为数组长度。

EtruriaLegend commented 2 years ago

思路

devosend commented 2 years ago

思路

滑动窗口

代码


class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0

        max_len = 1
        start = 0
        end = 0
        for i in range(len(s)):
            end  = i
            if start == end:
                continue

            # 使用Set优化时间复杂度
            if s[i] in s[start:end]:
                max_len = max(max_len, end - start)
                while s[i] in s[start:end] and start <= end:
                    start += 1
            else:
                max_len = max(max_len, end - start + 1)

        return max_len

复杂度分析

shibingchao123 commented 2 years ago

var lengthOfLongestSubstring = function(s) { const hash = new Set() let res = 0, right = -1 //右指针从左指针左边开始 for(let left = 0; left < s.length; left++){ if(left != 0){ hash.delete(s.charAt(left-1)) } while(right+1 < s.length && !hash.has(s.charAt(right+1))){ hash.add(s.charAt(right+1)) right++ } res = Math.max(res, right - left + 1) } return res };

linyang4 commented 2 years ago

思路

滑动窗口 维护2个索引, left和right, 用来控制窗口的边界; 创建一个set用于存储窗口里的值, 先移动right索引, 然后取字符串对应索引的值.

代码

var lengthOfLongestSubstring = function(s) {
    let ans = 0
    let left = 0
    let right = 0
    const charSet = new Set()
    while(right < s.length) {
        const rightChar = s[right]
        while(charSet.has(rightChar)) {
            const leftChar = s[left]
            left++
            charSet.delete(leftChar)
        }
        charSet.add(rightChar)
        right++
        ans = Math.max(ans, charSet.size)
    }
    return ans
};

复杂度

q815101630 commented 2 years ago

Sliding window