3.无重复字符的最长子串

作者 : XiaXinyu
日期 :2021-09-23

题目
理解

在一个给定字符串中找出不重复的最长字串的长度

题解1(朴素做法——使用HashSet)

我首先想到使用朴素做法来解决这道题目,

即枚举出所有字符串组合,

判断每个字符串中是否符合无重复字符的条件,

符合条件的字符串中的最长字符串长度即为答案

代码
public int LengthofLongestSubstring(String s){
 		int n = s.length();
  	int ans = 0;
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j <= n; j++) 
            if (allUnique(s, i, j)) ans = Math.max(ans, j - i);  //在allUnique方法中i,j区间是左闭右开的
    return ans;
}

public boolean allUnique(String s, int start, int end) {
    Set<Character> set = new HashSet<>();
    for (int i = start; i < end; i++) {
        Character ch = s.charAt(i);
        if (set.contains(ch)) return false;
        set.add(ch);
    }
    return true;
}
朴素法时间复杂度分析

需要使用两层for循环来枚举出所有字符串组合

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

判断字符串是否包含重复字符,

使用hashset,时间复杂度为O(n)

这样总体的时间复杂度是O(n^3),这就陷入了TLE的困境,显然是不能AC的

朴素法空间复杂度分析

由于hashset中是不包含重复元素的,所以最多开辟O(m)的空间,m为字符串s的字符集大小

题解2 (滑动窗口做法——使用HashSet)

在题解一中,我们枚举了每种可能的字符串组合

做了太多重复的工作

实际上,当[i,j]区间中包含重复字符串时j指针就不用后移了

可以采用滑动窗口实现这种做法

伪代码
  • 初始化n为字符串s长度,右指针r = -1,ans初始为0,初始化字符集set
  • 当左指针i小于字符串长度时执行循环
    • 如果i不等于0,则左指针右移一位
    • 否则进入循环,滑动窗口开始滑动(其实就是r指针右移哈哈哈哈)
      • 当r + 1小于n且set中不包含r + 1所指向的字符时执行循环
      • 将r + 1所指向的字符加入set
      • r指针右移
      • ans 更新成max(ans,r - i + 1)
  • 返回答案ans
模拟样例

输入s = “pwwkew” 输出 3

初始状态,set为空,左指针指向0,r指针开始右移

此时字符集中以存在r + 1所指向的内容

while循环终止,答案ans更新为2

set删除左指针所指内容,左指针右移

此时仍不满足while循环条件

set删除左指针所指内容,左指针右移

此时满足while循环条件

滑动窗口开始滑动

此时不满足while循环条件

set删除左指针所指内容,左指针右移

ans更新为3

while循环条件满足 r指针右移

ans更新为3

因为r指针已经到达字符串末尾且此时ans = 3

所以ans 的最大值一定为3

接下来i指针右移过程不做演示

代码
class Solution{
    public static int lengthOfLongestSubstring(String s){
        HashSet<Character> set = new HashSet<>();
        int n = s.length();
        int r = -1;
        int ans = 0;
        for(int i = 0;i < s.length();i ++){
            if(i != 0){
                set.remove(s.charAt(i - 1));
            }
            while(r + 1 < n && ! set.contains(s.charAt(r + 1))){
                set.add(s.charAt(r + 1));
                r ++;
            }
            ans = Math.max(ans,r - i + 1);
        }
        return ans;
    }
}

时间复杂度:因为需要从字符串的每个位置开始滑动窗口的滑动操作,但是两个指针分别只会遍历字符串一次,所以遍历的时间复杂度为O(n),

空间复杂度:O(m),m为字符串s的字符集大小

题解三(滑动窗口——不使用HashSet)

因为所有输入字符的ASCII码在[0,130]之间

所以可以开辟一个数组来记录每个字符的出现次数

依然采用滑动窗口的方式进行操作

伪代码
  • 初始化数组a,用来记录字符出现次数
  • 初始化左指针i,右指针j为0,当右指针小于字符串s的长度时执行循环
    • 右指针i指向字符的出现次数加1
    • 如果右指针指向的字符出现次数大于1则进入while循环
      • 左指针j指向的字符出现次数减1
      • 左指针右移
    • ans更新为max(ans,i - j + 1)
  • 返回答案值ans
代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s.length() == 0)
            return 0;
        int[] a = new int[130];
        int ans = 0;
        for(int i = 0,j = 0;i < s.length();i ++){
            a[(int)s.charAt(i)] ++;
            while(a[(int)s.charAt(i)] > 1){
                a[(int)s.charAt(j)] --;
                j ++; 
            }
            ans = Math.max(ans,i - j + 1);
        } 
        return ans;
    }
}

时间复杂度:O(n)

空间复杂度:O(130),由于开辟数组a存储字符出现次数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

XiaXinyuuu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值