算法通过村第十二关-字符串|白银笔记|经典面试题

news2025/1/21 17:55:49

文章目录

  • 前言
  • 1. 反转问题
    • 1.1 反转字符串
    • 1.2 k个一组反转
    • 1.3 仅仅反转字母
      • 1.3.1 采用栈实现操作
      • 1.3.2 采用双指针实现操作
    • 1.4 反转字符串里面的单词
      • 1.4.1 使用语言提供的方法来解决(内置API)
      • 1.4.2 如何优雅自己实现上述功能
  • 2. 验证回文串
  • 3. 字符串中的第一个唯一字符
  • 4. 判断是否互为字符重排
      • 4.1 排序重组
      • 4.2 采用Hash实现操作
  • 总结


前言


提示:越是在意的人,越是绝口不提。我选择你,做内心深处的秘密。来日有人剖开这颗心来,里面除了你,什么都没有。 --陶立夏《生活的比喻》

本章挑战的难度不大,是大量字符串题目的基础。务必学扎实。

1. 反转问题

我们知道反转是链表的一个重要考点,反转同样也是字符串的一个重要问题。常见的也就是如下这些:

推荐题目⭐⭐⭐⭐⭐:

344. 反转字符串 - 力扣(LeetCode)

541. 反转字符串 II - 力扣(LeetCode)

917. 仅仅反转字母 - 力扣(LeetCode)

151. 反转字符串中的单词 - 力扣(LeetCode)

557. 反转字符串中的单词 III - 力扣(LeetCode)

这些题目你是否看出一些规律在其中,要么反转字符串,要么反转里面的单词。针对字符的反转可以变换造出很多题目来。这里我们据从基本出发,逐个击破。

1.1 反转字符串

参考题目介绍:344. 反转字符串 - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
这是最基本的反转题,也是最简单的问题。这里使用双指针最直接。具体的做法是:

对于一个长度为N的待反转的字符串数组,我们观察反转前后的下标的变化,就可以知道结果

s[0]   s[N - 1]
s[1]   s[N - 2]
s[2]   s[N - 3]
s[3]   s[N - 4]
s[4]   s[N - 5]
...    ...
    
s[n - 1]   s[0]

比较完之后我们可以发现规律:即s[i] 与 s[N - 1 -i] 发生了交换。所以双指针我们就可以这样写:

  • 将left指向字符串数组的首元素,right指向字符串数组的尾元素
  • 当left < right:
    • 交换s[left] 和 s[right]
    • left 指针右移一位,即left = left + 1
    • right指针左移一位,即right = tight -1
  • 当left >= right ,反转结束,返回字符数组即可
    /**
         * 反转字符串(双指针)
         * @param s the string 
         */
    public static void reverseString(char[] s) {
       int n = s.length;
       for(int left = 0,right = n - 1; left < right; left++,right--){
           char c = s[left];
           s[left] = s[right];
           s[right] = c;
       }
    }

1.2 k个一组反转

参考题目介绍:541. 反转字符串 II - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
怎么感觉这题在找事呢?确实是闲了。这里我们根据题意直接模拟结果:

反转每个下标从2k的倍数开始,长度为k的字串。如果字串长度不足k则反转整个字串。

/**
     * 反转k个字符串
     * @param s
     * @param k
     * @return
     */
    public static String reverseStr(String s, int k) {
        if(s == null || s.length=0){
            return new String();
        }
       int n = s.length();
       char[] chars = s.toCharArray();
       for(int i = 0; i < n; i += 2*k){
           reverse(chars,i,Math.min(k + i,n) -1);
       }
       return new String(chars);

    }

    public static void reverse(char[] arr, int left, int right) {
       while(left < right){
           char c = arr[left];
           arr[left] = arr[right];
           arr[right] = c;
           left++;
           right--;
       }
    }

1.3 仅仅反转字母

参考题目介绍:917. 仅仅反转字母 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述
这里第一眼的感觉是不是很复杂,但同样从两头向中间就可以了,但是问题‘-’不是均匀的有些麻烦,处理起来不容易就增加了难度。其实换一种思路就不同了,栈这个工具,对于反转来说非常好用。

1.3.1 采用栈实现操作

具体方法:

将s中的所有字母单独存放在栈中,所以出栈来说就是反序操作了(当然也可以用数组存并反序的)

然后遍历s的所有字符,如果是字母就输出。

     /**
         * 仅仅反转字母
         * @param s the string to reverse
         * @return
         */
    public static String reverseOnlyLetters(String s) {
        // 校验参数
        if (s == null || s.length() == 0) {
            return s;
        }
        // 创建一个栈作存储单词
        Stack<Character> letters = new Stack<Character>();
        for (Character c : s.toCharArray()){
            if (Character.isLetter(c)){
                letters.push(c);
            }
        }
        StringBuilder res = new StringBuilder();
        for (Character c : s.toCharArray()){
            if (Character.isLetter(c)){
                res.append(letters.pop());
            }else{
                res.append(c);
            }
        }
        return res.toString();
    }

1.3.2 采用双指针实现操作

一个接一个输出s的所有字符,当遇到一个字母时,我们希望找到逆序遍历字符串的下一个字母。

所以我们这么做:维护一个指针j从后向前遍历字符串,当需要字母的时候就使用它。

    /**
         * 进反转字母(双指针实现)
         * @param s the string to reverse
         * @return
         */
    public static String reverseOnlyLetters2(String s) {
        // 校验参数
        if (s == null || s.length() == 0) {
            return s;
        }
        StringBuilder res = new StringBuilder();
        int j = s.length() - 1;
        for(int i = 0; i < s.length(); i++) {
            if (Character.isLetter(s.charAt(i))){
                // 可能不止一个
                while(!Character.isLetter(s.charAt(j))){
                    j--;
                }
                res.append(s.charAt(j--));
            }else{
                res.append(s.charAt(i));
            }
        }
       return   res.toString();
    }

1.4 反转字符串里面的单词

参考题目介绍:151. 反转字符串中的单词 - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
这个题目经常在很多面试题中见到,这样的题目重点在如何处理单词,其实难度并不大,我们重点看下分析:

1.4.1 使用语言提供的方法来解决(内置API)

比如Java,Python,C++等很多语言提供了相关的特性,因此我们可以首先使用语言的特性来解决:

很多语言对字符串都有特殊的处理,提供了split(拆分),reverse(反转) 和 join(连续)等方法。一次采用内置的API是可行的。

  • 使用split将字符串按照空格分割成字符串数组;
  • 使用reverse将字符串数组进行反转;
  • 使用join方法将字符串重新拼接成一个字符串。

如图:

在这里插入图片描述

public String reverseWords(String s) {
        if(s == null || s.length() == 0){
            return s;
        }
        // 去除开头和结尾的空白字符,这个很重要
        s = s.trim();
        // 正则匹配连续的字符空白字符作为分隔符
        // 正则表达式中\s匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        Collections.reverse(wordList);
        return String.join(" ",wordList);
}

上面的方法,在面试中式禁止使用的,别想了,怎么会这么优雅呢,还是乖乖自己实现吧。

1.4.2 如何优雅自己实现上述功能

对于字符串可变的语言,就不需要在额外开辟空间了,直接在字符串上原地实现,这种情况下,反转字符和去除空格可以一起完成。

    /**
         * 反转字符串中的单词
         * @param s
         * @return
         */
    public static String reverseWords(String s) {
        // 校验参数
        if(s == null || s.length() == 0) {
            return s;
        }
        // 出去多余空白字符
        StringBuilder sb = trimSpaces(s);

        // 反转字符串
        reverse(sb,0,sb.length() - 1);

        // 反转每个单词
        reverseWord(sb);

        return sb.toString();
    }

    private static void reverseWord(StringBuilder sb) {
        int n = sb.length();
        int start = 0, end = 0;
        while(start < n){
            // 循环到单词末尾
            while(end < n && sb.charAt(end) != ' '){
                ++end;
            }
            // 反转单词
            reverse(sb,start,end - 1);
            // 更新start, 去寻找下一个单词
            start = end + 1;
            ++end;
        }
    }

    /**
     * 反转字符出(可变)
     * @param sb
     * @param left
     * @param right
     */
    private static void reverse(StringBuilder sb, int left, int right) {
        while (left < right){
            char temp = sb.charAt(left);
            sb.setCharAt(left++,sb.charAt(right));
            sb.setCharAt(right--,temp);
        }
    }

    private static StringBuilder trimSpaces(String s) {
        int left = 0,right = s.length() - 1;
        // 去除掉开头和结尾的空白字符
        while (left <= right && s.charAt(left) == ' ') {
            left++;
        }

        while (left <= right && s.charAt(right) == ' ') {
            right--;
        }

        // 将字符串间多余的空白符去掉
        StringBuilder sb = new StringBuilder();
        while(left <= right){
            char c = s.charAt(left);
            if (c != ' '){
                sb.append(c);
            }else  if (sb.charAt(sb.length() - 1) != ' '){
                sb.append(c);
            }
            left++;
        }
        return sb;
    }

2. 验证回文串

参考题目地址:125. 验证回文串 - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
回文问题再链表中也是常见的,再字符串中同样也是重点。(这里记录一下骨头哥☠️的经历技术面式:

第一就式写一个判断字符串回文的问题,题目本身很简单,双指针的典型问题。下面就会被加餐,找最长的回文串(这里就需要动态规划的思想了,这里我们后面再说))

说是话,刷了这么多题目,这个问题真的式手到擒来。有很多思路。最简单的方法就是对字符串进行一次遍历,并保留其中的字母和数字,放入另一个字符串sgood中。这样我们只需要判断sgood是不是一个普通的回文串了。

如果不是用语言的特性,这里我们可以使用双指针的思想来处理。

  • 初始,左右指针分别指向sgood的两侧
  • 不断向中间移动,判断是否相同,如果相遇就说明是回文串
     /**
         * 判断是否为回文字符串
         * @param s
         * @return
         */
    public static boolean isPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        StringBuilder sgood = new StringBuilder();
        int len = s.length();
        for(int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (Character.isLetterOrDigit(c)) {
                sgood.append(Character.isLowerCase(c));
            }
        }
        int n = sgood.length();
        int left = 0,right = n - 1;
        while(left < right) {
            if (Character.toLowerCase(sgood.charAt(left)) != Character.toLowerCase(sgood.charAt(right))) {
                return false;
            }
            ++left;
            --right;
        }
        return true;
    }

3. 字符串中的第一个唯一字符

参考题目地址:387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

在这里插入图片描述

对于这个问题,最直观的方法就是遍历两次就可以知道结果,采用hash统计字符串中每个字符出现的次数。在第二次遍历时,就可以遍历到只出现1次的字符,直接返回他的索引即可,没有的话返回-1。

    /**
         * 找只出现一次的字符
         * @param s
         * @return
         */
    public static int firstUniqChar(String s) {
        // 参数校验
       if(s == null || s.length() == 0){
           return 0;
       }
       Map<Character,Integer> frequency = new HashMap<Character,Integer>();
       for(int i = 0; i < s.length(); i++){
           char c = s.charAt(i);
           frequency.put(c,frequency.getOrDefault(c,0) + 1);
       }
       for(int i = 0; i < s.length(); i++){
           if (frequency.get(s.charAt(i)) == 1){
               return i;
           }
       }
       return -1;
    }

4. 判断是否互为字符重排

参考题目介绍:242. 有效的字母异位词 - 力扣(LeetCode)

在这里插入图片描述

这是一道简单题,就不要想的特别复杂啦。(当然这也是一道非常经典的题目)

这个题目来说,如果你觉得时排列组合的问题,还解决了,说明你排列组合学的很好。但是这里有更好的解法。

4.1 排序重组

将两个字符串全部从小到大或者从大到小排序,然后再逐个比较,不管原始字符串是什么,都可以判断出来,代码也不复杂。

      /**
         * 判断字符串是否重排
         * @param s1
         * @param s2
         * @return
         */
    public static boolean checkPermutation1(String s1, String s2) {
       // 将字符串转成数组
        char[] chars1 = s1.toCharArray();
        char[] chars2 = s2.toCharArray();
        // 排序
        Arrays.sort(chars1);
        Arrays.sort(chars2);
        // 比较
        return new String(chars1).equals(new String(chars2));
    }

这里需要注意我们使用了内置api,面试是不允许的。

4.2 采用Hash实现操作

注意:这里我们不能简单的判断是否已经存在,因为字符串可能在某个串里面重复存在例如“abac”。所以我们可以记录出现的次数,如果一个字符串经过重新排列后,能变成另外一个字符串,那么他们每个不同字符出现的次数是相同的。例如:出现此处不同的,那么便是两个字符串不能够经过重新排列得到。

所以写起来页不复杂,但是长了些。

  /**
     * 判断字符串是否重排
     * @param s1
     * @param s2
     * @return
     */
    public static boolean checkPermutation(String s1, String s2) {
        if(s1.length() != s2.length()){
            return false;
        }
        char[] chars = s1.toCharArray();
        Map<Character, Integer> s1Map = getMap(s1);
        Map<Character, Integer> s2Map = getMap(s2);
        for(Character c : chars){
            if(!s1Map.containsKey(c) || !s2Map.containsKey(c) || s1Map.get(c).intValue() != s2Map.get(c).intValue() ){
                return false;
            }
        }
        return  true;

    }

    /**
     * 统计指定字符串str中各字符的出现次数,并以Map的形式返回
     * @param str
     * @return
     */
    public static Map<Character, Integer> getMap(String str) {
        Map<Character, Integer> map = new HashMap<>();
        char[] chars = str.toCharArray();
        for (char aChar : chars) {
            map.put(aChar, map.getOrDefault(aChar, 0) + 1);
        }
        return map;
    } 

总结

提示:字符串常见问题;双指针问题的回顾;反转问题;回文字符串;重排序问题。


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。
在这里插入图片描述

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

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

相关文章

SaaS 电商设计 (二) 全链路解决方案概述和核心业务流程梳理

一.业务目标&技术目标 业务目标:完成多业态,多渠道的数字化运营 自有业务: O2O,B2C,B2B2C,S2B2b 平台业务:POPB2c,POPB2b,POPS2B2b 1.1 自有业务 O2O:全称Online to Offline.泛指的线上线下的业务融合.这种的情况分为两种情况,第一种通过线上的数字化运营引导线上用户线下…

oCPC实践录 | oCPC转化的设计、选择、归因与成本设置(4)

读者们&#xff0c;中秋十一快乐&#xff0c;一篇文章奉上。 在上篇oCPC实践录 | oCPC转化的设计、选择、归因与成本设置&#xff08;3&#xff09;文章中我们主要讨论&#xff1a;怎么定义高价值用户&#xff0c;回传高价值用户不一定利益最大化&#xff0c;回传高ROI用户逻辑…

速度轴模拟量控制FB(博途SCL+三菱ST代码)

利用模拟量实现变频器的正反转直接控制具体方法,请参考下面文章链接: 模拟量0-10V信号控制变频器实现正反转速度随动_RXXW_Dor的博客-CSDN博客比例随动专栏有系列文章介绍,大家可以查看相关文章,链接如下:绕线机-排线伺服比例随动功能块(梯形图+SCL代码)_RXXW_Dor的博客…

自己实现扫描全盘文件的函数。

1.自己实现扫描全盘的函数 def scan_disk(dir): global count,dir_count if os.path.isdir(dir): files os.listdir(dir) for file in files: print(file) dir_count 1 if os.path.isdir(dir os.sep file): …

MATLAB:线性系统的建模与仿真(含完整程序)

目录 前言实验内容一、先看作业题目要求二、作业正文Modeling LTI systemsEstablish model1.tf(sys2)2. tf(sys3)3.zpk(sys1)4. zpk(sys3)5. ss(sys1)6. ss(sys2)7.[num,den] tfdata(sys1)8.[num,den] tfdata(sys2)9.[num,den] tfdata(sys3)10.[num,den] tfdata(sys1,’v’…

【Linux】信号简介与触发信号的几种方式

相信大家或多或少都听说过信号这一概念&#xff0c;这篇博客将为大家讲解一些信号方面的基础知识 信号简介 定义Linux操作系统中重要的消息机制功能主要被用于进程干预处置&#xff0c;大多数信号的作用是杀死或挂起进程&#xff0c;只有少部分的信号不是用来杀死或挂起进程个…

【数据结构】排序(3)—堆排序归并排序

目录 一. 堆排序 基本思想 代码实现 向上调整算法 向下调整算法 时间和空间复杂度 稳定性 二. 归并排序 基本思想 代码实现 时间和空间复杂度 稳定性 一. 堆排序 堆排序&#xff08;Heapsort&#xff09;是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似 …

lambda表达式在实际开发中的使用

作为写代码已经两年的程序员了&#xff0c;lambda已经是再熟悉不过了。其实在众多的编程语言中&#xff0c;python javascript java中都有lambda的影子。包括比较新的编程语言golang&#xff0c;到最后发现其实各种语言的语法和特性都是相互抄袭的&#xff0c;所以在接触新技术…

drone和gogs安装

背景介绍 什么是 Drone&#xff1f; Drone 是一个面向忙碌的开发团队的自助持续集成和持续交付平台。官网地址&#xff1a; https://www.drone.io/同时&#xff0c;Drone 是使用 Golang 语言进行编写。所有的编译、测试的流程都在 Docker 容器中执行。Drone 通过使用简单的 YA…

直线导轨坏了可以维修吗?

直线导轨是工业自动化设备中常用的零部件&#xff0c;其性能和使用寿命对设备的稳定运行和产能有着直接的影响&#xff0c;在生产中&#xff0c;由于各种原因&#xff0c;直线导轨会出现各种问题&#xff0c;那么&#xff0c;直线导轨的维修方法究竟是怎样的呢&#xff1f;我们…

竞赛选题 深度学习 opencv python 实现中国交通标志识别_1

文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 &#x1f525; 优质…

【安鸾靶场】实战渗透

文章目录 前言一、租房网 (150分)二、企业网站 (300分)三、SQL注入进阶 (550分) 前言 最近看到安鸾的靶场有些比较有意思就打了一下午&#xff0c;有一定难度。 一、租房网 (150分) http://106.15.50.112:8031/ 刚打开burp就报了thinkphp的代码执行 直接getshell flag&a…

邮件注册(一)验证码发送

通过邮箱实现注册&#xff0c;用户请求验证码完成注册操作。 导入依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><dependency><g…

PACS系统 PACS系统源码一种用于存储、管理和传输医学影像数据的系统

医用软件中的影像归档与传输系统软件&#xff08;Picture Archiving and Communication System&#xff0c;简称PACS&#xff09;是一种用于存储、管理和传输医学影像数据的系统。其主要功能包括&#xff1a; 影像存储&#xff1a;PACS可以将医学影像数据以数字化的形式存储在服…

C语言之自定义类型_结构体篇(2)

目录 结构体传参 结构体实现位段&#xff08;位段的填充&可移植性&#xff09; 什么是位段 位段的大小计算 位段的内存分配 位段的跨平台问题 位段的应用 今天接着我们继续自定义类型结构体。&#x1f642;&#x1f642; 结构体传参 在我们初阶结构体我们学习过结…

Spring web security

儅使用spring的web security時&#xff0c;默認會轉向自帶的spring security example page。而不會轉向error page。 TODO: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> &l…

城市、机场、服务区等场景下的智慧公厕建设诀窍揭秘

在如今繁忙的生活节奏中&#xff0c;人们对于公共服务设施的需求越来越高。而智慧公厕正是把传统公共厕所转变为智慧化的场所&#xff0c;得到了极大的欢迎。目前&#xff0c;各行各业均对公共厕所进行信息化、智慧化、数字化的升级&#xff0c;尤其是在机场、服务区和城市等场…

基于SSM的电子相册系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

6.MySql连接SqlYog

MySql连接SqlYog SqlYog和navicat均是数据可视化工具&#xff0c;熟悉其一即可 SqlYog下载安装 连接&#xff0c;密码和端口号一定要正确&#xff01;&#xff01;&#xff01; 2.保存到数据库 创建数据库&表 创建数据库 创建成功 创建表 点击保存 查看表数据的…

基于javaweb的智慧社区设计与实现

目录 前言 一、技术栈 二、系统功能介绍 客户信息管理 客户信息管理 社区信息管理 车位租买支付 前台车位信息 车位预定提交 问卷调查管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理…