【LeetCode】剑指 Offer Ⅱ 第3章:字符串(7道题) -- Java Version

news2024/11/14 1:28:16

题库链接:https://leetcode.cn/problem-list/e8X3pBZi/

在这里插入图片描述

题目解决方案
剑指 Offer II 014. 字符串中的变位词双指针 + 数组模拟哈希表 ⭐
剑指 Offer II 015. 找到字符串中所有字母异位词双指针 + 数组模拟哈希表 ⭐
剑指 Offer II 016. 不含重复字符的最长子字符串双指针 + 数组模拟哈希表 ⭐
剑指 Offer II 017. 最小覆盖子串双指针 + 哈希表 ⭐
剑指 Offer II 018. 有效的回文双指针(前后)⭐
剑指 Offer II 019. 最多删除一个字符得到回文双指针(前后)⭐
剑指 Offer II 020. 回文子字符串的个数双指针(从回文中心出发)⭐

本章的题目主要分为两种类型:变位词 和 回文;
……
如果将 字符串 看成一个由字符组成的数组,那么也可以用两个指针来定位一个子字符串,其中一个指针指向字符串的第1个字符,另一个指针指向字符串的最后一个字符,两个指针之间所包含的就是一个子字符串。
……
一般来说,就双指针的字符串问题而言,这种类型的面试题基本上都与 统计字母出现的次数 有关,我们常用 哈希表 来存储每个元素出现的次数。

1. 剑指 Offer II 014. 字符串中的变位词 – P31

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。

1.1 滑动窗口 – O(m+n)

时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( n ) O(n) O(n)

窗口定长为 n,每次向前移动一格.

class Solution {
    // 1. 滑动窗口
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        
        if (n > m) return false;  // 如果 n > m,则 s2 中必不存在 s1 的变位词
 
        int[] cnt1 = new int[26];  // 26个英文字母
        int[] cnt2 = new int[26];
    
        for (int i = 0; i < n; i++) {
            ++cnt1[s1.charAt(i) - 'a'];
            ++cnt2[s2.charAt(i) - 'a'];
        }

        if (Arrays.equals(cnt1, cnt2)) return true; 

        for (int i = n; i < m; i++) {
            ++cnt2[s2.charAt(i) - 'a'];
            --cnt2[s2.charAt(i-n) - 'a'];

            if (Arrays.equals(cnt1, cnt2)) return true;
        }
        return false;
    }
}

在这里插入图片描述

1.2 双指针 + 数组模拟哈希表 – O(m+n)(⭐)

时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    // 2. 双指针 + 数组模拟哈希表
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m) {
            return false;
        }
        int[] cnt = new int[26];
        for (int i = 0; i < n; i++) --cnt[s1.charAt(i) - 'a'];  // 让 s1 全为负数

        for (int l = 0, r = 0; r < m; r++) {  // 双指针遍历 s2,从 s2 中找出 s1 的变式
            int t = s2.charAt(r) - 'a';
            cnt[t]++;

            while (cnt[t] > 0) --cnt[s2.charAt(l++) - 'a'];

            if (r - l + 1 == n) return true;  // 说明此时 cnt 数组之和全为 0
        }
        return false;
    }
}

在这里插入图片描述

2. 剑指 Offer II 015. 找到字符串中所有字母异位词 – P35

给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
……
此题也可使用1题中 滑动窗口 的解法,但是滑动窗口 + 两个 cnt[] 的方法要比双指针的慢一些,为避免冗余,此处省略.

2.1 双指针 + 数组模拟哈希表 – O(m+n)(⭐)

时间复杂度 O ( m + n ) O(m+n) O(m+n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    // 双指针
    public List<Integer> findAnagrams(String s, String p) {
        int n = s.length(), m = p.length();
        
        List<Integer> list = new LinkedList<>();
        
        int[] cnt = new int[26];  // 26个小写英文字母

        for (int i = 0; i < m; i++) --cnt[p.charAt(i) - 'a'];  // 初始化 p 串, 使cnt--

        for (int l = 0, r = 0; r < n; r++) {  // 双指针移动 s 串
            int t = s.charAt(r) - 'a';
            cnt[t]++;

            while (cnt[t] > 0) --cnt[s.charAt(l++) - 'a'];

            if (r - l +  1 == m) list.add(l);  // 在 s 中找到 p 的变位词,记录起始索引
        } 
        return list;
    }
}

在这里插入图片描述

3. 剑指 Offer II 016. 不含重复字符的最长子字符串 – P36

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

3.1 双指针 + 数组模拟哈希表 – O(n)(⭐)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    // 1. 双指针 + 数组模拟哈希表
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        if (n == 0) return 0;

        int res = 0;
        int[] ascii = new int[256];  // 与前两题不同的是,这次不局限于小写英文字母了

        for (int l = 0, r = 0; r < n; r++) {
            int t = s.charAt(r);
            ascii[t]++;

            while (ascii[t] > 1) --ascii[s.charAt(l++)];  // ascii[i] > 0, 说明出现了重复字符,开始移动 l 

            res = Math.max(res, r - l + 1);  // 记录不重复子字符串的长度
        }
        return res;
    }
}

在这里插入图片描述

3.2 HashSet – O(n)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set<Character> set = new HashSet<Character>();
        int n = s.length();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int r = -1, ans = 0;
        for (int l = 0; l < n; l++) {
            if (l != 0) {
                // 左指针向右移动一格,移除一个字符
                set.remove(s.charAt(l - 1));
            }
            while (r + 1 < n && !set.contains(s.charAt(r + 1))) {
                // 不断地移动右指针
                set.add(s.charAt(r + 1));
                ++r;
            }
            // 第 l 到 r 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, r - l + 1);
        }
        return ans;
    }
}

在这里插入图片描述

PS:补充知识1 - 【HashSet】

在这里插入图片描述

💡 HashSet集合(元素无序且唯一),其底层是哈希表,特点:

  • 无序;
  • 不可重复;
  • 不能排序;

……
元素的唯一性是靠元素重写 hashCode() 和 equals() 方法来保证的,如果不重写则无法保证唯一性.(Integer以及String类等都已经重写了这两个方法,一定要注意自己定义的类需要重写才能确保唯一性!)
……
更多内容可参考:
[1] 单列集合Set的实现类HashSet - 陪雨岁岁年年的博客
[2] Java-单列集合(Collection,List,Set集合的详细介绍)- 壹万捌先生的博客

4. 剑指 Offer II 017. 最小覆盖子串 – P39

给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。

4.1 双指针 + 哈希表 – O(n)(⭐)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    public String minWindow(String s, String t) {
        int n = s.length(), m = t.length();

        Map<Character,Integer> tmap = new HashMap<>();
        for (int i = 0; i < m; i++) tmap.put(t.charAt(i), tmap.getOrDefault(t.charAt(i),0)+1);

        int count = 0;
        Map<Character,Integer> smap = new HashMap<>();

        String res = "";

        for (int l = 0, r = 0; r < n; r++) {
            char rch = s.charAt(r);
            smap.put(rch, smap.getOrDefault(rch,0)+1);
            if (smap.get(rch) <= tmap.getOrDefault(rch,0)) {  // 排除重复冗余字符
                count++;
            }

            while (smap.getOrDefault(s.charAt(l),0) > tmap.getOrDefault(s.charAt(l),0) && l < r) {  // 当子字符串超过所需匹配字符时,移动l
                smap.put(s.charAt(l), smap.getOrDefault(s.charAt(l),0)-1);
                l++;
            }

            if (count == m) {
                if (res.isEmpty() || (r - l + 1) < res.length()){  // 比较找出最小字符串
                    res = s.substring(l, r + 1);
                }
            }
        }
        return res;
    }
}

在这里插入图片描述

4.2 双指针 + 数组模拟哈希表 – O(n)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

相比于4.1要更快,但是相对也要更难记忆.

class Solution {
  public String minWindow(String s, String t) {
      int[] map = new int[256];
      
      for(char c: t.toCharArray()){  // 初始化 t 字符串
        map[c]++;
      }
      
      int minLen = Integer.MAX_VALUE, minStart = 0;  // 设定最短字符串的长度和起始位置
  
      int m = t.length(), n = s.length();
      char[] sc = s.toCharArray();

      for (int l = 0, r = 0; r < n; r++){
        int rch = sc[r];
        map[rch]--;
        if(map[rch] >= 0){  // 减小与 t 的字符差距
          m--; 
        }
        
        if(m == 0){  // 成功在 s 中匹配到包含 t 的子字符串
          int lch = sc[l];
          while(map[lch] < 0){  // 开始尝试缩小窗口范围
            map[lch]++;
            l++;
            lch = sc[l];  
          }
          
          int len = r - l + 1;
          if(len < minLen){  // 找出最小子字符串
            minLen = len;
            minStart = l;
          }
          
          map[lch]++;
          l++;
          m++;
        }
      }
      return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen);
    }
}

在这里插入图片描述

5. 剑指 Offer II 018. 有效的回文 – P42

给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。

5.1 双指针(前后)-- O(n)(⭐)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    // 双指针:需要注意的是,有效字符包括:英文字母、数字和空格
    public boolean isPalindrome(String s) {
        int n = s.length();
        char[] sch = s.toLowerCase().toCharArray();

        for (int l = 0, r = n-1; l < r;) {
            if (!Character.isLetterOrDigit(sch[l])) l++;  // 判断是否为英文字母或数字
            else if (!Character.isLetterOrDigit(sch[r])) r--;
            else {
                if (sch[l] != sch[r]) return false;  // 前后指针不相同,说明非回文
                l++;
                r--;
            }
        }
        return true;
    }
}

在这里插入图片描述

PS:补充知识1 - 【Character包装类 – 8种方法】

判断一个字符是否是数字、英文、其他字符.

// 1. 判断该字符是否是字母,是返回true,否返回false
Character.isLetter(char ch)
// 2. 判断一个字符是否是数字字符,是返回true,否返回false
Character.isDigit(char ch)	
// 3. 判断该字符是字母还是数字,如果字符是字母或数字返回true; 否则false。
Character.isLetterOrDigit(char ch)
// 4. 判断指定字符是否为空白字符,空白符包含:空格、tab 键、换行符
Character.isWhitespace(char ch) 
// 5. 判断该字符是否是小写字母,是返回true,否返回false
Character.isLowerCase(char ch)
// 6. 判断该字符是否是大写字母,是返回true,否返回false
Character.isUpperCase(char ch)
// 7. 将小写字符转换为大写
Character.toUpperCase('A')
// 8. 将大写字符转换为小写
Character.toUpperCase('a')
// 9. 用于返回一个表示指定 char 值的 String 对象。结果是长度为 1 的字符串,仅由指定的 char 组成。
Character.toString('a')

6. 剑指 Offer II 019. 最多删除一个字符得到回文 – P43

给定一个非空字符串 s,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。

6.1 双指针(前后)-- O(n)(⭐)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

class Solution {
    public boolean validPalindrome(String s) {
        int n = s.length();
        char[] sch = s.toCharArray();
        int l = 0, r = n-1;
        for (; l < r; l++, r--) {
            if (sch[l] != sch[r]) break;  // 找到第一次不同之处
        }
        return isPalindrome(l+1,r,sch) || isPalindrome(l,r-1,sch);  // 删左或删右
    }

    public boolean isPalindrome(int l, int r, char[] sch) {  // 再次利用双指针进行回文判断
        while (l < r) {
            if (sch[l] != sch[r]) return false;
            l++;
            r--;
        }
        return true;
    }
}

在这里插入图片描述

6.2 双指针(前后)+StringBuilder – O(n)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

速度并不会提升,且书写代码量似乎也不会渐少,总之不太推荐这种写法。

class Solution {
    public boolean validPalindrome(String s) {
        int n = s.length();
        int l = 0, r = n - 1;
        while (l < r) {
            if (s.charAt(l) != s.charAt(r)) break;
            l++;
            r--;
        }
        if (r <= l) return true;

        String ls = s.substring(l+1, r+1);  // substring(), 左闭右开
        StringBuilder stringBuilder = new StringBuilder(ls);
        String rs = s.substring(l, r);
        StringBuilder stringBuilder2 = new StringBuilder(rs);
        return ls.equals(stringBuilder.reverse().toString()) || rs.equals(stringBuilder2.reverse().toString());
    }
}

在这里插入图片描述

PS:补充知识1 - 【substring() 方法】

在这里插入图片描述

substring() 方法返回字符串的子字符串,左闭右开区间 [beginIndex, endIndex)。

public class RunoobTest {
    public static void main(String args[]) {
        String Str = new String("This is text");
 
        System.out.print("返回值 :" );
        System.out.println(Str.substring(4) );  // 返回值 : is text
 
        System.out.print("返回值 :" );
        System.out.println(Str.substring(4, 10) );  // 返回值 : is te
    }
}

7. 剑指 Offer II 020. 回文子字符串的个数 – P44

给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。

7.1 双指针(从回文中心出发)-- O(n2)(⭐)

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)

class Solution {
    // 双指针:从回文中心出发
    public int countSubstrings(String s) {
        int n = s.length();

        if (n <= 1) return n;

        int count = 0;

        char[] sch = s.toCharArray();

        for (int i = 0; i < n; i++) {
            count += countPalindrome(sch, i, i);  // 奇数长度:一个对称中心
            count += countPalindrome(sch, i, i+1);  // 偶数长度:两个对称中心
        }
        return count;
    }

    public int countPalindrome(char[] sch, int l, int r) {
        int count = 0;
        while (l >= 0 && r < sch.length && sch[l] == sch[r]) {
            count++;
            l--;
            r++;
        }
        return count;
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/839777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue2-vue中mixin到底是什么?

1、mixin是什么&#xff1f; Mixin是面向对象程序设计语言中的类&#xff0c;提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类。 Mixin类通常作为功能模块使用&#xff0c;在需要该功能时“混入”&#xff0c;有利于代码的复用又避免了多继承的复杂。 1.1 vue中…

LeetCode643. 子数组最大平均数 I

题干 给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你找出平均数最大且 长度为 k 的连续子数组&#xff0c;并输出该最大平均数。 任何误差小于 10^-5 的答案都将被视为正确答案。 示例1&#xff1a; 输入&#xff1a;nums [1,12,-5,-6,50,3], k 4 输出&am…

Matlab中图的最短路径

前言&#xff1a; 图的基本概念&#xff1a; 若想简单绘制图可以利用此网站&#xff1a; 左上角Undirected/Directed是无向图/有向图 左边 0-index &#xff0c;1-index为0下标&#xff0c;1下标。 Node Count为节点个数 Graph Data&#xff1a;最初尾节点的名称&#xff…

HDFS小文件解决方案---archive归档文件命令

小文件解决方案 背景Archive概述创建archive查看归档文件查看归档之后的样子查看归档文件之前的样子 提取archivearchive注意事项 背景 hdfs并不擅长存储小文件&#xff0c;因为每个文件最少一个block&#xff0c;每个block的元数据都会在namenode占用内存&#xff0c;如果存在…

WAF绕过-漏洞利用篇-sql注入+文件上传-过狗

WAF绕过主要集中在信息收集&#xff0c;漏洞发现&#xff0c;漏洞利用&#xff0c;权限控制四个阶段。 1、什么是WAF&#xff1f; Web Application Firewall&#xff08;web应用防火墙&#xff09;&#xff0c;一种公认的说法是“web应用防火墙通过执行一系列针对HTTP/HTTPS的安…

Java thymeleaf bug排查记录

刚学Java 做项目时报了一个错误 一时间看的莫名其妙 EL1008E: Property or field createTime cannot be found on object of type java.util.HashMap - maybe not public or not valid? 随即向上排查至第一个报错&#xff0c;发现是thymeleaf渲染时报错。 Exception proces…

ChatGPT下架官方检测工具,承认无法鉴别AI内容

去年底&#xff0c;OpenAI 推出的 ChatGPT &#xff0c;带来了生成式人工智能涌现的热潮。它不仅能够协助完成撰写邮件、视频脚本、文案、翻译、代码等任务&#xff0c;还能通过学习和理解人类的语言来进行对话&#xff0c;并根据聊天的上下文进行互动。 但随之而来的争议也让人…

代码随想录算法训练营day55

文章目录 Day55 判断子序列题目思路代码 不同的子序列题目思路代码 Day55 判断子序列 392. 判断子序列 - 力扣&#xff08;LeetCode&#xff09; 题目 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以…

Java课设--学生信息管理系统(例1)

文章目录 前提一、运行效果二、Text实现类三、Manage选择类四、StudentWay学生方法类五、StudnetSql数据库类 前题 例1为无使用GUI图形界面&#xff0c;例2使用GUI图形界面&#xff01; 首先自己的JDBC驱动已经接好了&#xff0c;连接自己的数据库没有问题。连接数据库可以看…

Linux Day06

目录 一、printf输出问题 二、复制进程fork 2.1进程 2.2 pid_t fork(void); 注意&#xff1a; 2.3逻辑地址和物理地址 2.4写时拷贝技术 一、printf输出问题 printf 函数并不会直接将数据输出到屏幕&#xff0c;而是先放到缓冲区中&#xff0c;只有一下三种情况满 足&a…

2.文件的逻辑结构

第四章 文件管理 2.文件的逻辑结构 顺序文件采用顺序存储则意味着各个逻辑上相邻的记录在物理上也是相邻的存储的。所以如果第0号记录的逻辑地址为0的话&#xff0c;则i号记录的逻辑为i *L。 特别的如果这个定长记录的顺序文件采用串结构&#xff0c;也就是这些记录的顺序和他…

Python爬虫——爬虫时如何知道是否代理ip伪装成功?

前言 在进行爬虫时&#xff0c;我们可能需要使用代理IP来伪装自己的身份&#xff0c;以避免被网站封禁。如何判断代理IP是否伪装成功呢&#xff1f;本篇文章将围绕这个问题展开讲解&#xff0c;同时提供Python代码示例。 1. 确认代理IP地址 首先&#xff0c;我们需要确认代理…

unity制作FPS射击游戏

文章目录 介绍鼠标移动控制视角行走、奔跑、跳跃、下蹲射击、后坐力、射速、瞄准、弹痕、枪火、抛壳手臂摇摆手枪切枪效果动画状态机玩家血量新地图场景颜色渐变激光墙获取钥匙滑动门NPC属性攻击逻辑终点传送门 介绍 角色动作方面包括行走、奔跑、跳跃、武器切换、弹夹更换、武…

下载Windows 10光盘镜像(ISO文件)

文章目录 下载Windows 10镜像文件 下载Windows 10镜像文件 打开微软官网下载地址 立即下载工具 找到下载工具&#xff0c;双击运行&#xff0c;等待 接受条款&#xff0c;等待 选择为另一台电脑安装介质 选择Windows10&#xff0c;下一步 选择ISO文件&#xff0c;…

嵌入式开发学习(STC51-7-矩阵按键)

内容 按下S1-S16键&#xff0c;对应数码管最左边显示0-F 矩阵按键简介 独立按键与单片机连接时&#xff0c;每一个按键都需要单片机的一个I/O 口&#xff0c;若某单片机系统需较多按键&#xff0c;如果用独立按键便会占用过多的I/O口资源&#xff1b;而单片机 系统中I/O口资…

搜索是什么

1、什么是搜索&#xff1f; 搜索&#xff1a;计算机根据用户输入的关键词进行匹配&#xff0c;从已有的数据库中摘录出相关的记录反馈给用户。 常见的全网搜索引擎&#xff0c;有百度、谷歌这样搜索网站。 除此&#xff0c;搜索技术在垂直领域也有广泛的使用&#xff0c;比如淘…

利用awk筛选给定时间范围内的日志

文章目录 筛选给定时间范围内的日志时间时间戳什么是时间戳&#xff1f; 系统时间 筛选日志时间示例简单示例mktime()函数是什么 进阶示例 筛选给定时间范围内的日志 时间 时间的表示方法&#xff1a; 时间戳系统时间&#xff08;年月日时间&#xff09; 时间戳 什么是时间…

Spring Boot读取yml或者properties配置信息

文章目录 Spring Boot读取yml或者properties配置信息方法一&#xff1a;Value获取基本信息&#xff0c;适用于少量信息方法二&#xff1a;通过注解ConfigurationProperties(prefix "spring.datasource")方法三&#xff1a;通过api Environment Spring Boot读取yml或…

Leetcode-每日一题【剑指 Offer 09. 用两个栈实现队列】

题目 用两个栈实现一个队列。队列的声明如下&#xff0c;请实现它的两个函数 appendTail 和 deleteHead &#xff0c;分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素&#xff0c;deleteHead 操作返回 -1 ) 示例 1&#xff1a; 输入&#xff1a; [&…

Java判断文件的系统格式编码格式

使用Java判断一个文件的系统格式&#xff08;亲测可用&#xff09;&#xff0c;比如我们常见的Windows格式的文件&#xff0c;Unixg格式的文件&#xff0c;Mac格式的文件&#xff1b;常常有这样的场景&#xff1a;我们在Windows系统编写的脚步上传到Linux系统执行&#xff0c;执…