无重复字符的最长字串

题目

给定一个字符串 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. 返回最大长度。

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);: 在每一步迭代中,更新最长子串的长度。

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确实比我块的多,但是时间复杂度很大。懂得都懂

  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五敷有你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值