无重复字符的最长字串

题目

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

示例

示例 1:

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

示例 2:

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

示例 3:

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

思想(滑动窗口)

滑动窗口是一种在数组或字符串上进行迭代的算法,通过维护一个子数组或子串,该子数组或子串的大小在迭代过程中可以变化。该子数组或子串的起始位置通过"滑动"进行调整,从而找到符合特定条件的解。

滑动窗口的窗口大小是动态变化的,当发现重复字符时,通过调整窗口的起始位置来实现。这样,你能够在遍历字符串的过程中找到不包含重复字符的最长子串,同时利用 `max` 变量来记录最大长度。

滑动窗口是解决一类子串或子数组问题的常见思想,通常用于解决找到不重复元素的最长子串或子数组的问题。这种方法的优势在于它可以在线性时间内解决问题,而不需要嵌套循环。

算法分析

  1. 初始化变量:`max` 用于存储最长子串的长度,`index` 用于跟踪重复字符的索引,空字符串 `str` 用于存储当前子串。

  2. 使用 for 循环遍历输入字符串 `s` 中的每个字符。

  3. 在循环内部,通过使用 `indexOf` 方法检查当前字符 `ch` 是否已经存在于当前子串 `str` 中。如果存在(`index != -1`),则表示找到了重复的字符。

  4. 如果找到重复的字符,通过比较当前子串 `str` 的长度和当前最大长度 `max` 来更新 `max` 长度。然后,通过删除从子串开头到重复字符(包括重复字符)的字符来更新子串 `str`,并将当前字符 `ch` 连接到子串中。

  5. 继续循环。

  6. 如果未找到重复字符,简单地将当前字符 `ch` 连接到子串 `str` 中。

  7. 循环结束后,比较最终子串 `str` 的长度和当前最大长度 `max`,以确保最后一个子串也被考虑在内。

  8. 返回最大长度。

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int max=0;
        int index=-1;
        String str="";
   
        for(int i=0;i<s.length();i++){
            char ch=s.charAt(i);
            index=str.indexOf(ch);
           if(index!=-1){
              
              max=Math.max(str.length(),max);
              str=s.substring(i-(str.length()-1-index),i+1);
            
                continue;
           }
             str=str+ch;
        }
          max=Math.max(str.length(),max);
        return max;
    }
}

运行结果

总的时间复杂度为 O(n)

击败百分之5%的对手。。。emmm被我击败南坪

算法调优

这时官方的解法,思想也是滑动窗口,来看看有什么区别:

时间复杂度是O(n^2)

它采用一个Set集合,然后左指针指向i ,右指针指向rk

rk从一个开始逐个遍历,增加不同的值加入集合,

一旦有相同的值,就说明此i指向的值的最长长度已经到了,让左指针i++,指向下一个值。

rk不用更新回去,因为从i到rk是不重复的,那从i+1到rk也肯定是不重复的,所以rk不用更新回去,set集合也不用清空。

每次左指针移动前,要更新最长的长度

Set<Character> occ = new HashSet<Character>();: 创建一个哈希集合 occ,用于记录当前窗口中字符的出现情况。

int rk = -1, ans = 0;: 定义右指针 rk 初始值为 -1,用于表示当前窗口的右边界。ans 用于记录最长子串的长度。

occ.remove(s.charAt(i - 1));: 在每次移动左指针时,从哈希集合中移除左指针所指向的字符,表示该字符不再属于当前窗口。

while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) { ... }: 在每次移动右指针时,不断地向右移动,直到遇到重复字符或者到达字符串的末尾。在移动的过程中,将新的字符加入哈希集合。

ans = Math.max(ans, rk - i + 1);: 在每一步迭代中,更新最长子串的长度。

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;

    }
}

时间耗时:6ms确实比我块的多,但是时间复杂度很大。懂得都懂

相关推荐
地平线开发者2 小时前
征程 6M 部署 Omnidet 感知模型
算法·自动驾驶
秋说3 小时前
【PTA数据结构 | C语言版】线性表循环右移
c语言·数据结构·算法
浩瀚星辰20243 小时前
图论基础算法:DFS、BFS、并查集与拓扑排序的Java实现
java·算法·深度优先·图论
古希腊被code拿捏的神4 小时前
【Flutter】面试记录
flutter·面试·职场和发展
JiaJZhong5 小时前
力扣.最长回文子串(c++)
java·c++·leetcode
oioihoii5 小时前
C++随机打乱函数:简化源码与原理深度剖析
开发语言·c++·算法
不知名。。。。。。。。6 小时前
分治算法---快排
算法
minji...6 小时前
数据结构 算法复杂度(1)
c语言·开发语言·数据结构·算法
凌肖战6 小时前
力扣网编程150题:加油站(贪心解法)
算法·leetcode·职场和发展
吃着火锅x唱着歌6 小时前
LeetCode 3306.元音辅音字符串计数2
算法·leetcode·c#