【面试题】最长不含重复字符的子字符串(三种解法,Java、Python实现)

题目描述

在这里插入图片描述

解法一:动态规划+哈希表查找

Java

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.isEmpty()) return 0; // 等同于判断 s.length()是否等于0
        HashMap<Character, Integer> map = new HashMap<>(); // 创建一个hashmap用于记录各字符最后一次出现的索引位置
        int curLength =0, maxLength = 0; // curLength 记录当前不含重复字符的子串长度,maxLength 记录全局的最长不重复子串的长度
        for(int i=0; i<s.length(); i++){
            int preIndex = map.getOrDefault(s.charAt(i),-1); // 获取最近的上一个与当前字符相同的字符所在索引位置
            if(preIndex < 0 || i > preIndex + curLength) // 若之前还没有遇到相同字符,或者上一个重复字符出现在当前不重复子串之外,则子串长度+1
                curLength++;
            else // 否则说明上一个重复字符出现在当前不重复子串之中,则子串左边界更新为上一个重复字符的下一个位置,即保留 [preIndex+1, i]范围内的字符。
                curLength = i - preIndex;
            map.put(s.charAt(i), i); // 更新哈希表
            maxLength = Math.max(maxLength, curLength); // 更新全局最长不重复子串的长度
        }
        return maxLength;
    }
}

Python

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        dic = {}
        curLength, maxLength = 0, 0
        for i in range(len(s)):
            preIndex = dic.get(s[i], -1)
            if preIndex < 0 or i - preIndex > curLength:
                curLength +=1
            else:
                curLength = i - preIndex
            dic[s[i]] = i
            maxLength = max(maxLength, curLength)
        return maxLength

在这里插入图片描述

解法二:动态规划+线性查找

Java

左边界 i 获取方式: 遍历到 s[j] 时,初始化索引 i = j - 1,向左遍历搜索第一个满足 s[i] = s[j] 的字符即可 。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> dic = new HashMap<>();
        int res = 0, tmp = 0;
        for(int j = 0; j < s.length(); j++) {
            int i = j - 1; // 当遍历到字符 s[j]时,初始化索引 i=j-1,向左遍历搜索第一个满足 s[i]=s[j]的字符
            while(i >= 0 && s.charAt(i) != s.charAt(j)) i--; // 线性查找 i
            tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
            res = Math.max(res, tmp); // max(dp[j - 1], dp[j])
        }
        return res;
    }
}

Python

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        dic = {}
        res, tmp = 0, 0
        for i in range(len(s)):
            left = i - 1
            while left >= 0 and s[left] != s[i]:
                left -= 1
            if i - left > tmp:
                tmp += 1
            else:
                tmp = i - left
            res = max(res, tmp)
        return res

在这里插入图片描述

解法三:双指针 + 哈希表

Java

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> dic = new HashMap<>(); // 哈希表用于记录每个字符最后一次出现的位置
        int i = -1, res = 0; // 左指针 i始终指向不重复字符子串的左边界
        for(int j = 0; j < s.length(); j++) {
            if(dic.containsKey(s.charAt(j))) // 判断当前遇到的字符是否之前出现过
                i = Math.max(i, dic.get(s.charAt(j))); // 如果发现重复了,更新左指针 i为上一个重复字符的位置,保证区间[i+1,j]内无重复字符且长度最大
            dic.put(s.charAt(j), j); // 更新哈希表中字符 s[j]最后出现的位置
            res = Math.max(res, j - i); // 比较上轮 res和本轮双指针区间 [i+1,j]的宽度(即 j - i),更新最大长度
        }
        return res;
    }
}

Python

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        dic = {}
        left = -1
        res = 0
        for i in range(len(s)):
            if s[i] in dic:
                left = max(left, dic.get(s[i])) # 更新左指针
            dic[s[i]] = i
            res = max(res, i-left)
        return res

复杂度分析:

  • 时间复杂度:O(N),for循环遍历字符串中的每个字符,需要 O(N) 的时间
  • 空间复杂度:O(1),字符的 ASCII 码范围为 0 ~ 127 ,哈希表 dic 最多使用 O(128) 即 O(1) 大小的额外空间。

参考

LeetCode详细题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值