LeetCode题目 无重复字符的最长子串 中等

题目

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

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

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

示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。

请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

思路

又是一个字符串的题,这里就需要吸取上次的教训了。空字符…算了,是0,按常规流程走也直接是整型的初始值了。

因为需要找的是无重复字符的子串的最大长度,因此区别于子序列,字符之间必须是连续的才行。

我们首先以程序的方式思考一下。
找子串,就需要对字符串进行一个个字符的遍历,需要从第一个字符开始计算子串并同步当前找到的子串的长度。
由于找的是最大长度,因此只要没有遇到重复的字符就可以一直加下去。但是如果遇到重复的字符怎么办?

重复的字符是没办法遇见其位置的,可能是开头,也可能是结尾,也可能是中间任意一个位置。比如ababcd,遍历到第一个ab,然后会遇到第二个a,这个的a在开头;abcddce则在dd遇到重复的d,而这个d在abcd的结尾;abcdebec则是在ebe这里首先遇到重复的字符b,它在abcde的中间位置,是整个字符串的第二个位置。

那么如何解决这个办法呢?因为遇到重复就表示当前子串已达到了目前子串的最大值,因此先行将此长度与目前最长的子串长度作比较,更新最长的长度数值。然后,由于子串不能包含重复的字符,因此下一个子串要从最近的无重复字符开始,也就是从被重复的字符位置开始。举个例子:abcdebec这个字符串,ebe这里首先重复了b,那么在更新了最长长度后,下一个子串的计算就应该从被重复的b开始,也就是abcde变更为cde,然后再加上重复了的b,成为新子串:cdeb。这个时候,新的无重复字符子串长度是4。接下来遇到e,又是一个重复,那么现在维护的子串就应该从cdeb变成b,再加上e,也就是be

也就是说,在遍历的时候,需要维护一个子串变量最长长度变量
每一次循环都要检查这个字符是否重复了,如果重复了,就按照上边的方法处理;如果没有重复,就加入到维护的子串变量中并更新最长长度变量。

代码

C#代码:

public class Solution {
    public int LengthOfLongestSubstring(string s) {
        var length = 0;
        var t = "";

        for (int i = 0, t_length = 0; i < s.Length; i++)
        {
            if (t.Contains(s[i]))
            {
                if (length < t_length)
                {
                    length = t_length;
                }

                t += s[i];
                t = t.Substring(t.IndexOf(s[i])+1);
                t_length = t.Length;
            }
            else
            {
                t += s[i];
                t_length++;
                if (i + 1 == s.Length && length < t_length)
                {
                    length = t_length;
                }
            }
        }

        return length;
    }
}

官方解法

滑动窗口法

基础思路的图片介绍:
滑动窗口:列举发现规律
滑动窗口算法讲解
检查字符串是否包含某字符(即重复字符):
检查重复字符
可以看到的是,官方使用了双指针+哈希的方法来一遍移动指针,一边计算非重复子串长度。
每一次移动右指针,都要先检测当前子串是否包含了右指针对应的字符(使用哈希表),如果包含了(也就是重复了),就需要移动左指针来去除重复,直到不重复为止才能继续移动右指针

官方代码

java代码:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set<Character> occ = new HashSet<Character>();
        int n = s.length();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        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;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetc-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

滑动窗口法的解释

什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
时间复杂度:O(n)O(n)

作者:powcai
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-powcai/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

感想

前边也做了字符串的题,这里也是个字符串的题。由于字符串其实是连续的字符数组,因此见到的解法都是使用指针完成的。指针的执行效率当然是更高的,之后再遇到字符串题,我也会尝试用指针解决一下问题的。涉及到字符重复的检测,也就是字符串中是否包含某个字符,应当直接想到唯一性的数据结构,比如说一系列的键值对类型(必须保持键的唯一性)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值