剑指offer48.最长不含重复字符的子字符串

我一开始的想法是创建一个大小为26的int数组,下标为0对应的是'a',25对应的是'z',然后一开始都赋为-1,用一个for循环从头遍历这个字符串,通过char c = s.charAt(i)获得字符,然后c-97,就是它对应的int数组的下标,然后访问这个下标下的元素,如果是-1,那么count++,然后把i赋给这个int数组的这个下标下的元素,如果不是-1,说明前面已经出现过了,然后就把这个下标下的值赋给i,让i从上一次出现的位置的后一个重新开始扫描(这次循环后i会+1所以赋给i的下标上的值而不是下标上的值+1),count归零,数组全部归零,整个期间用max记录count的最大值,最后返回max。

java 复制代码
class Solution {
    int[] visit = new int[26];
    public int lengthOfLongestSubstring(String s) {
        renew();
        int n = s.length();
        int count=0, max=0;
        for(int i=0;i<n;i++){
            char c = s.charAt(i);
            if(c==32)return 1;
            if(c<97)continue;
            int index = c - 97;
            if(visit[index] == -1){
               count++;
               max = Math.max(max, count);
               visit[index] =i;
            }else{
               i = visit[index];
               count=0;
               renew();
            }
        }
      return max;     
    }
    public void renew(){
        for(int i = 0;i<26;i++){
            visit[i] = -1;
        }
    }
}

但是示例里面千奇百怪的字符串,比如这个

这样遇到第3个b的时候会到!上去,最后还是放弃了,看了题解,题解如下:

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashSet<Character>  occ = new HashSet<Character>();
        int n = s.length();
        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++;
           }
           ans = Math.max(ans, rk - i +1);
        } 
        return ans;
    }
}

题解用的是双指针,左指针先不动,右指针往右移动直到遇见重复的,长度就是右-左,它是用一个HashSet来判断重复,每遍历一个如果set里面没有这个字母,就把这个字母放进set里面,如果有就说明重复了。但是题解中的右指针rk,他并没有拿rk去试探是不是重复,而是拿rk+1去看右指针右边这个是不是重复,所以可以看见字符串的长度不是rk-i而是rk-i+1,并且每次遇到重复rk并不会更新,因为i到rk+1是重复的,说明i+1到rk肯定不是重复的(因为i到rk肯定不是重复的),而且左指针每次移动都要把set里面的上一个左指针的字母删掉,这样可以保证set里面存的都是左指针到右指针之间的字母,没有前面的字母,所以可以看见occ.remove(s.charAt(i-1));发现了重复后进入了下一次循环,所以i要减1,才是删掉了上一个左指针。

还有题解是用动态规划写的:

动态规划+哈希表:

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> dic = new HashMap<>();
        int res = 0, tmp = 0;
        for(int j = 0; j < s.length(); j++) {
            int i = dic.getOrDefault(s.charAt(j), -1); // 获取索引 i
            dic.put(s.charAt(j), j); // 更新哈希表
            tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
            res = Math.max(res, tmp); // max(dp[j - 1], dp[j])
        }
        return res;
    }
}

动态规划+线性遍历:

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0, tmp = 0;
        for(int j = 0; j < s.length(); j++) {
            int i = j - 1;
            while(i >= 0 && s.charAt(i) != s.charAt(j)) i--; // 线性查找 i
            tmp = tmp < j - i ? tmp + 1 : j - i; // dp[j - 1] -> dp[j]
            res = Math.max(res, tmp); // max(dp[j - 1], dp[j])
        }
        return res;
    }
}

双指针+哈希表:

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> dic = new HashMap<>();
        int i = -1, res = 0;
        for(int j = 0; j < s.length(); j++) {
            if(dic.containsKey(s.charAt(j)))
                i = Math.max(i, dic.get(s.charAt(j))); // 更新左指针 i
            dic.put(s.charAt(j), j); // 哈希表记录
            res = Math.max(res, j - i); // 更新结果
        }
        return res;
    }
}
相关推荐
xiaobai12 32 分钟前
二叉树的遍历【C++】
开发语言·c++·算法
Freak嵌入式10 分钟前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
java·开发语言·数据结构·python·接口·抽象基类
前端小马20 分钟前
解决IDEA出现:java: 程序包javax.servlet不存在的问题
java·servlet·intellij-idea
吱吱鼠叔27 分钟前
MATLAB数学规划:2.线性规划
算法·机器学习·matlab
声学黑洞仿真工作室35 分钟前
Matlab Delany-Bazley和Miki模型预测多孔材料吸声性能
开发语言·人工智能·算法·matlab·微信公众平台
机器学习之心39 分钟前
选址模型 | 基于混沌模拟退火粒子群优化算法的电动汽车充电站选址与定容(Matlab)
算法·选址模型
IH_LZH1 小时前
Broadcast:Android中实现组件及进程间通信
android·java·android studio·broadcast
去看全世界的云1 小时前
【Android】Handler用法及原理解析
android·java
.Net Core 爱好者1 小时前
Redis实践之缓存:设置缓存过期策略
java·redis·缓存·c#·.net
晚睡早起₍˄·͈༝·͈˄*₎◞ ̑̑1 小时前
苍穹外卖学习笔记(五)
java·笔记·学习