当前位置: 首页 > news >正文

LeetCode03 - 无重复字符的最长子串(Java 实现)

LeetCode03 - 无重复字符的最长子串(Java 实现)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters

题目描述

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

示例 1:

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

示例 2:

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

示例 3:

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

相关知识点补充

Java 数组的下标可以是:

  • 整型常量
  • 整型变量
  • 整型字符常量(字符的 ASCII 码)
  • 整型表达式

例如:

int a[300],i;
// 下标是整型常量,整数 5
a[5] = 5; 

// 下标是整型变量, i 现在数值 5, 就是数组元素 a[5]。
i = 5;
a[i] = 5; 

// 下标是整型字符常量,等于 F 的 ASCII 码值,就是数组元素 a[70]。
a['F'] = 5; 

// 下标是整型表达式,表达式运算结果是 5,就是数组元素 a[5]。
a['F' - 'A'] = 5; 

怎么查看 ASCII 码的值?

// 比如查看 a 的 ASCII 值
System.out.println((int)'a');
// 结果为 97

System.out.println((int)'z');
// 结果为 122

Java 实现与实现思路

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 03:给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。
 *
 * @author XiaoPengwei
 * @since 2019-07-14
 */
public class LC03LongestSubstring {

    public static void main(String[] args) {

        // 该代表性示例,最长为 4,即 abcd
        // String str = "abcabcdbcs";
        String str = "abba";

        int lengthByMethod1 = lengthOfLongestSubstringMethod1(str);
        System.out.println("lengthByMethod1-->" + lengthByMethod1);

        int lengthByMethod2 = lengthOfLongestSubstringMethod2(str);
        System.out.println("lengthByMethod2-->" + lengthByMethod2);
    }

    /**
     * 方法一:
     * 求不含有重复字符的最长子串的长度
     * 思路:创建一个 pre 数组表示长度,从左到右遍历字符串数组,查看
     *
     * @param s 字符串参数
     * @return int 长度
     */
    public static int lengthOfLongestSubstringMethod1(String s) {

        // 数组没有赋值的时,所有元素会初始化为 0
        // 字符为下标时,会将 ASCII 码作为下标
        int[] pre = new int[128];

        int max = 0, t = 0;

        // i 表示当前处理的第 i 个字符
        for (int i = 0; i < s.length(); i++) {

            // c 为依次取出的单个字符
            char c = s.charAt(i);

            /* 如果 pre[c] 不等于 0 表示数组中该位置被修改过,也就代表前面有重复字符
             */
            if (pre[c] != 0 && pre[c] > t) {

                // 更新 max 最大值
                // i - t 重复元素下标 - 上一次没重复的下标
                max = Math.max(max, i - t);

                // t 是为求下一个子串长度做准备,因为要求出的是最长的子串长度
                // 更新 t,上一次没重复的下标
                t = pre[c];
            }

            // 如果 pre[c] 为 0,或者 pre[c] <= t
            pre[c] = i + 1;
        }

        return Math.max(max, s.length() - t);
    }

    /**
     * 方法二:
     * 定义一个 map 数据结构存储 (k, v),其中 key 值为字符,value 值为字符位置 +1,加 1 表示从字符位置后一个才开始不重复
     * 我们定义不重复子串的开始位置为 start,结束位置为 end; [start, end] 可理解为滑动窗口
     * 随着 end 不断遍历向后,会遇到与 [start, end] 区间内字符相同的情况,此时将字符作为 key 值,获取其 value 值,并更新 start,此时 [start, end] 区间内不存在重复字符
     * 无论是否更新 start,都会更新其 map 数据结构和结果 max。
     * 时间复杂度:O(n)
     *
     * @param s 字符串参数
     * @return int 长度
     * @author guanpengchn
     * @link https://leetcode-cn.com/problems/two-sum/solution/hua-jie-suan-fa-3-wu-zhong-fu-zi-fu-de-zui-chang-z/
     */
    public static int lengthOfLongestSubstringMethod2(String s) {

        // max 表示所求的最大长度
        int max = 0;

        /* Map 中 key 值为字符,value 值为字符位置 +1,加 1 表示从字符位置后一个才开始不重复
         * 同一个字符 key,在 map 中只存在一个。当重复时更新它的值。
         */
        Map<Character, Integer> map = new HashMap<Character, Integer>();

        /* start 指向不重复子串的第一个字符的下标。
         * 当存在重复字符时 start 指向后面一个重复字符,指向下一个不重复子串的第一个字符的下标
         */
        for (int start = 0, end = 0; end < s.length(); end++) {

            char c = s.charAt(end);

            if (map.containsKey(c)) {

                /* 如果含有重复字符串,将滑动窗户的开始位置更新,后移
                 * map.get(c) 的值表示该出现重复的字符,上一次出现时下标+1
                 * 什么时候会出现 start<map.get(c)?
                 * 答:map.get(c) 该重复字符出现的位置不一定,如果重复字符出现的顺序和之前一样,
                 * 比如:abcabcd,先重 a,再重 b
                 * 什么时候会出现 start>map.get(c)?
                 * 答:第二次出现重复时,如果重复的字符在第一次出现重复的字符前面
                 * 比如 abba,先重 b,再重 a。出现 a 重复时,start 为 2 > map.get('a') 为 1
                 */
                start = Math.max(map.get(c), start);
            }

            /* 不管是否重复这里都会执行
             * 每次执行判断一次滑动窗口长度是否超过当前最大长度,是则更新
             * end - start + 1 表示滑动窗口长度,子串长度,不重复子串最短也为 1
             * 为什么要 + 1?
             * 当 start 和 end 指向一个是元素时,下标一样,end-start 为 0,此时存在一个不重复子串为 1 个元素
             * 当 end 指向 start 后面相邻一个,end-start 为 1,此时不重复子串为 2 个元素
             */
            max = Math.max(max, end - start + 1);

            /* 不管是否重复这里都会执行
             * 如果不重复,会新添加一个 key,值为:位置下标+1
             * 如果重复,会更新这个 key 对应的值为:后面又重复出现的该字符的位置下标+1
             */
            map.put(c, end + 1);
        }

        return max;
    }
}

相关文章:

  • Idea 获取 git 仓库时更新类型update type 的选择
  • Java 工具类 IpUtil - 获取本机所有 IP 地址,LocalHost 对应地址 IP
  • 8080 端口被占用的解决方法 netstat -ano;taskkill (命令行)
  • 手写 Spring MVC
  • Oracle 在 Drop 表时的 Cascade Constraints
  • Oracle:ORA-01219:database not open:queries allowed on fixed tables/views only
  • MyBatis: Invalid bound statement (not found) 错误的可能原因
  • Git 删除已经 Push 的远程文件夹或文件的命令方法
  • 写给自己 - 开发路上
  • ubuntu 18 自带截图工具 - 快捷键
  • svn 必须会敲的常用命令
  • ubuntu 18 解锁文件目录(谨慎操作)
  • ubuntu 18 安装 navicat Premium 中文乱码(很彻底)
  • 在 ubuntu 18 中为 navicat 创建快捷方式
  • You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).Please, commit your changes
  • 【347天】每日项目总结系列085(2018.01.18)
  • 0基础学习移动端适配
  • Laravel 菜鸟晋级之路
  • Mysql数据库的条件查询语句
  • PaddlePaddle-GitHub的正确打开姿势
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • VuePress 静态网站生成
  • 对超线程几个不同角度的解释
  • 分布式熔断降级平台aegis
  • 高度不固定时垂直居中
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 配置 PM2 实现代码自动发布
  • 前端js -- this指向总结。
  • 如何设计一个比特币钱包服务
  • 少走弯路,给Java 1~5 年程序员的建议
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 网络应用优化——时延与带宽
  • 用quicker-worker.js轻松跑一个大数据遍历
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​你们这样子,耽误我的工作进度怎么办?
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #stm32驱动外设模块总结w5500模块
  • (SpringBoot)第七章:SpringBoot日志文件
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (全注解开发)学习Spring-MVC的第三天
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (转) RFS+AutoItLibrary测试web对话框
  • (转载)利用webkit抓取动态网页和链接
  • ..回顾17,展望18
  • ./configure,make,make install的作用
  • .gitattributes 文件
  • .net framework profiles /.net framework 配置
  • .net 逐行读取大文本文件_如何使用 Java 灵活读取 Excel 内容 ?
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .Net中的设计模式——Factory Method模式