ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【滑动窗口】java求无重复字符的最长字串(4种方法)

2021-10-27 00:00:19  阅读:183  来源: 互联网

标签:right java int rightChar maxLen window 字串 滑动 left


前言:这个题我整整看了一个晚上,麻烦耐心看完。
题目给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

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

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

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

示例 4:
输入: s = “”
输出: 0
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

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

下面说一下答案思路:

  1. 首先要能想到最简单的思路和方法,就是双层循环,内部判断。代码如下:
// 1. 暴力解法:遍历数组的所有的区间,然后找到最长没有重复字符的区间
// 时间复杂度:O(n^3)
// 空间复杂度:O(n)
// 会超时
public int lengthOfLongestSubstring1(String s) {
    int n = s.length();
    if (n <= 1) return n;
    int maxLen = 1;

    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (allUnique(s, i, j)) {
                maxLen = Math.max(maxLen, j - i + 1);
            }
        }
    }

    return maxLen;
}

private boolean allUnique(String s, int start, int end) {
    Set<Character> set = new HashSet<>();
    for (int i = start; i <= end; i++) {
        if (set.contains(s.charAt(i))) {
            return false;
        }
        set.add(s.charAt(i));
    }
    return true;
}

作者:tangweiqun
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/jian-dan-yi-dong-javac-pythonjshua-dong-bff20/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  1. 这个题想要实现时间复杂度为O(n),就得使用【滑动窗口】的方法。那么什么是滑动窗口?

滑动窗口其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!如何移动?我们只要把队列的左边的元素移出就行了,直到满足题目要求!一直维持这样的队列,找出队列出现最长的长度时候,求出解。
那现在有个问题是哪个储存结构里不能存相同的元素呢?显然,能想到hash,并且优先想到的应该是HashSet,因为HashMap的键值对似乎对这个没什么用。那么代码如下:

// 2. 滑动窗口
// 时间复杂度:O(2n) = O(n),最坏的情况是 left 和 right 都遍历了一遍字符串
// 空间复杂度:O(n)
public int lengthOfLongestSubstring2(String s) {
    int n = s.length();
    if (n <= 1) return n;
    int maxLen = 1;

    int left = 0, right = 0;
    Set<Character> window = new HashSet<>();
    while (right < n) {
        char rightChar = s.charAt(right);
        while (window.contains(rightChar)) {
            window.remove(s.charAt(left));
            left++;
        }
        maxLen = Math.max(maxLen, right - left + 1);
        window.add(rightChar);
        right++;
    }

    return maxLen;
}

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

3.对于上面的代码,可以发现while(window.contains(rightChar))
这个循环其实很不人性化,按人的思维,直接把left移动到相同元素(左边的)+1的位置就好了,完全没必要一个一个的移除再判断。怎么解决这个问题?有个好方法就是用刚刚忽视的HashMap,将每个字符对应的下标作为对应关系,就可以直接找到想找的位置,因为HashMap的键是唯一不能重复的,所以键(这里也就是字符)对应的值(这里也就是下标)改变即可。代码如下:

// 3. 优化后的滑动窗口
// 时间复杂度:O(n)
// 空间复杂度:O(n)
public int lengthOfLongestSubstring3(String s) {
    int n = s.length();
    if (n <= 1) return n;
    int maxLen = 1;

    int left = 0, right = 0;
    Map<Character, Integer> window = new HashMap<>();
    while (right < n) {
        char rightChar = s.charAt(right);
        int rightCharIndex = window.getOrDefault(rightChar, 0);
        left = Math.max(left, rightCharIndex);
        maxLen = Math.max(maxLen, right - left + 1);
        window.put(rightChar, right + 1);
        right++;
    }

    return maxLen;
}

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

3.最后的最后,还学到了一种我到死都不会想到的方法。
因为用Map储存其实也是想用一下那个数字,而作为字符串本身,每个字符都有对应的0-127的ASCII值,就可以用长度为128的数组代替Map了,代码如下:

// 4. 追求程序的极致性能
// s 由英文字母、数字、符号和空格组成
public int lengthOfLongestSubstring(String s) {
    int n = s.length();
    if (n <= 1) return n;
    int maxLen = 1;

    int left = 0, right = 0;
    int[] window = new int[128];
    while (right < n) {
        char rightChar = s.charAt(right);
        int rightCharIndex = window[rightChar];
        left = Math.max(left, rightCharIndex);
        maxLen = Math.max(maxLen, right - left + 1);
        window[rightChar] = right + 1;
        right++;
    }

    return maxLen;
}

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

标签:right,java,int,rightChar,maxLen,window,字串,滑动,left
来源: https://blog.csdn.net/dsafghljk/article/details/120982093

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有