LeetCode - 3 无重复字符的最长子串

题目来源

3. 无重复字符的最长子串 - 力扣(LeetCode)

 

题目描述

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

示例 1

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

示例 2

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

示例 3

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

提示

  • 0 <= s.length <= 5 * 10^4
  • s 由英文字母、数字、符号和空格组成

题目解析

本题很容易想到需要使用滑动窗口解题。

即定义两个指针 L,R,初始时 L,R 都初始化为0,表示 s 串的 [L, R)区间为一个滑动窗口。我们需要保证滑窗内部不能含有重复字符。

那么如何高效地判断滑窗内是否含有重复字符呢?

这里我们可以定义一个128长度的数组index,或者一个哈希表来记录 R 指针扫描过的字符及其出现的索引位置。

这里定义128长度的数组的方式更通用一点。

为什么是128长度的数组呢?因为标准ASCII码表就是128个字符,每个字符都对应一个码值,码值范围是0~127,而这刚好对应上了128长度的数组索引。

R 指针向右移动扫描时,每当扫描到一个字符 c,我们就去index中查找,该字符上次出现的位置index[c]:

  • 如果 index[c] >= L,则说明当前 R 指针扫描到的字符 c 在当前滑窗内已经存在,其位置在 index[c] 处(L ≤ index[c] < R):
  1. 此时 [L, R-1] 范围子串就是一个不含重复字符的子串,记录此时符合要求的子串长度:(R - 1) - L + 1,保留最大的长度
  2. 为了保证滑窗内不含有重复字符,我们应该将移动 L = index[c] + 1,这样的话,滑窗内部就不会含有重复的 c 了 
  • 如果 index[c] < L,则说明当前 R 指针扫描的字符 c 并没有造成滑窗内产生重复字符

然后更新 index[c] = R,以及R++继续扫描。

按照上面步骤,我们仅需要遍历一轮字符串s,即可得到题解。

C源码实现

int lengthOfLongestSubstring(char* s) {
    // index数组用于记录s串中字符c出现的索引位置
    int index[128]; // 标准ASCII码表有128个基本字符,对应的ASCII码值是0~127
    for (int c = 0; c < 128; c++) {
        index[c] = -1;
    }

    // 滑窗范围 [l, r)
    int l = 0;
    int r = 0;

    // 记录最大长度
    int maxLen = 0;

    // 遍历s串的每个字符c
    while (s[r] != '\0') {
        char c = s[r];

        // 当前r指针指向的字符c, 如果在滑窗内存在了,则index[c] >= l
        if (index[c] >= l) {
            // 此时 [l, r) 左闭右开范围是一个无重复字符的子串, 子串长度为:(r - l) - l + 1
            maxLen = (int)fmax(maxLen, r - l);
            // 保证滑窗范围内无重复字符,则移动 l 到滑窗内重复字符c的右侧位置
            l = index[c] + 1;
        }

        // 纳入r指针
        index[c] = r;
        r++;
    }

    // 收尾处理
    return (int)fmax(maxLen, r - l);
}

C++源码实现

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // index数组用于记录s串中字符c出现的索引位置
        // 标准ASCII码表有128个基本字符,对应的ASCII码值是0~127
        vector<int> index(128, -1);

        // 滑窗范围 [l, r)
        int l = 0;
        int r = 0;

        // 记录最大长度
        int maxLen = 0;

        // 遍历s串的每个字符c
        while (r < s.length()) {
            char c = s[r];

            // 当前r指针指向的字符c, 如果在滑窗内存在了,则index[c] >= l
            if (index[c] >= l) {
                // 此时 [l, r) 左闭右开范围是一个无重复字符的子串, 子串长度为:(r - l) - l + 1
                maxLen = max(maxLen, r - l);
                // 保证滑窗范围内无重复字符,则移动 l 到滑窗内重复字符c的右侧位置
                l = index[c] + 1;
            }

            // 纳入r指针
            index[c] = r;
            r++;
        }

        // 收尾处理
        return max(maxLen, r - l);
    }
};

 

Java源码实现

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // index数组用于记录s串中字符c出现的索引位置
        int[] index = new int[128]; // 标准ASCII码表有128个基本字符,对应的ASCII码值是0~127
        Arrays.fill(index, -1);

        // 滑窗范围 [l, r)
        int l = 0;
        int r = 0;

        // 记录最大长度
        int maxLen = 0;

        // 遍历s串的每个字符c
        while (r < s.length()) {
            char c = s.charAt(r);

            // 当前r指针指向的字符c, 如果在滑窗内存在了,则index[c] >= l
            if(index[c] >= l) {
                // 此时 [l, r) 左闭右开范围是一个无重复字符的子串, 子串长度为:(r - l) - l + 1
                maxLen = Math.max(maxLen, r - l);
                // 保证滑窗范围内无重复字符,则移动 l 到滑窗内重复字符c的右侧位置
                l = index[c] + 1;
            }

            // 纳入r指针
            index[c] = r;
            r++;
        }

        // 收尾处理
        return Math.max(maxLen, r - l);
    }
}

 

Python源码实现

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # index字典用于记录s串中字符c出现的索引位置
        index = {}

        # 滑窗范围 [l, r)
        l = 0
        r = 0

        # 记录最大长度
        maxLen = 0

        # 遍历s串的每个字符c
        while r < len(s):
            c = s[r]

            # 当前r指针指向的字符c, 如果在滑窗内存在了,则index[c] >= l
            if c in index and index[c] >= l:
                # 此时 [l, r) 左闭右开范围是一个无重复字符的子串, 子串长度为:(r - l) - l + 1
                maxLen = max(maxLen, r - l)
                # 保证滑窗范围内无重复字符,则移动 l 到滑窗内重复字符c的右侧位置
                l = index[c] + 1
            
            # 纳入r指针
            index[c] = r
            r += 1
        
        # 收尾处理
        return max(maxLen, r - l)

JavaScript源码实现

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function (s) {
    // index数组用于记录s串中字符c出现的索引位置
    const index = new Map();

    // 滑窗范围 [l, r)
    let l = 0;
    let r = 0;

    // 记录最大长度
    let maxLen = 0;

     // 遍历s串的每个字符c
    while (r < s.length) {
        const c = s[r];

        // 当前r指针指向的字符c, 如果在滑窗内存在了,则index[c] >= l
        if (index.has(c) && index.get(c) >= l) {
            // 此时 [l, r) 左闭右开范围是一个无重复字符的子串, 子串长度为:(r - l) - l + 1
            maxLen = Math.max(maxLen, r - l);
            // 保证滑窗范围内无重复字符,则移动 l 到滑窗内重复字符c的右侧位置
            l = index.get(c) + 1;
        }

        // 纳入r指针
        index.set(c, r++);
    }

    // 收尾处理
    return Math.max(maxLen, r - l);
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值