代码随想录:字符串5-7

右旋字符串

题目

        字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。

        例如,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。

        输入:输入共包含两行,第一行为一个正整数 k,代表右旋转的位数。第二行为字符串 s,代表需要旋转的字符串。

        输出:输出共一行,为进行了右旋转操作后的字符串。

代码

import java.util.Scanner;  //Scanner的包要导入,util不要拼写错误

public class Main{
    public static void main(String[] args){
        
        Scanner sc = new Scanner(System.in);  //获取输入,System.in不要忘记写
        int k = sc.nextInt();  //假设=2
        String s = sc.next();  //如abcdefg
        
        StringBuilder sb = new StringBuilder(s);  //StringBuilder的首字母都是大写
        
        reverse(sb, 0, sb.length() - 1);  //翻转整个字符串,如gfedcba
        reverse(sb, 0, k - 1);  //翻转前k个字母,如fgedcba
        reverse(sb, k, sb.length() - 1);  //翻转后面的字母,如fgabcde
        
        System.out.println(sb.toString());
        
    }
    
    //翻转区间[i,j]的字符
    public static void reverse(StringBuilder sb, int i, int j){
        while(i < j){
            char tmp = sb.charAt(i);
            sb.setCharAt(i,sb.charAt(j));  //setCharAt函数首字母C和A是大写
            sb.setCharAt(j,tmp);
            i++;
            j--;
        }
    }
}

总结

1.思想

        题目要求右旋字符串,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。那么可以分为3个步骤,首先翻转整个字符串,变成"gfedcba",然后,翻转前k个字符,变成"fgedcba",最后,翻转后面剩下的字符,变成"fgabcde"。

2.算法流程

        首先,需要先用Scanner获取输入的k和字符串s,然后new一个StringBuild对象,在其上进行三种reverse的操作(因为String类型不能修改)。接着,就是分别调用3次reverse函数,区别分别是[0,s.length()-1],[0,k-1],[k,s.length()-1]。

        reverse函数我写的是左闭右闭区间,接收StringBuild对象sb,区间开始i和区间结束j三个参数。当i<j时,就一直交换首尾的字符,交换结束后把i++,j--即可。

3.语法问题

(1)import java.util.Scanner;  //Scanner在util包下,要记得导入,util不要拼写错误

(2)Scanner sc = new Scanner(System.in);  //用于获取输入,括号的System.in不要忘记写

(3)new StringBuilder(s);  //StringBuilder的首字母都是大写

(4)sb.setCharAt(i,sb.charAt(j));  //setCharAt函数首字母C和A是大写

28.找出字符串中第一个匹配项的下标

题目

        给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1 

示例 1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

示例 2:

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 

代码(KMP)

class Solution {
    public int strStr(String haystack, String needle) {

        //如果模式串为空,默认返回0
        if(needle.length() == 0){
            return 0;
        }

        //如果模式串长度大于文本串长度,肯定找不到,直接返回-1;
        if(needle.length() > haystack.length()){
            return -1;
        }

        int[] next = new int[needle.length()]; //创建前缀表next
        getNext(next,needle);  //计算next,方便不匹配时进行回退

        //遍历文本串
        int j = 0; //j代表模式串的下标
        for(int i=0;i < haystack.length();i++){

            //当文本串的字符和模式串的字符不相等,即出现冲突了,j要回退
            while(j > 0 && haystack.charAt(i) != needle.charAt(j))
            {
                j = next[j - 1];
            }
            //当文本串的字符和模式串的字符相等,继续向后遍历
            if(haystack.charAt(i) == needle.charAt(j)){
                j++;
            }
            //如果模式串已经成功匹配到最后,直接返回匹配下标
            if(j == needle.length()){
                return i - needle.length() + 1;
            }
        }
        return -1;  //如果前面没有匹配,最后返回-1
    }
    
    //计算前缀表next的值
    public void getNext(int[] next,String s){

        //初始化
        int j = 0; //j代表前缀末尾,j+1也就是相同前后缀长度了
        next[0] = 0; //第一个字母没有相同前后缀

        //i代表后缀末尾,从下标1开始变量模式串
        for(int i = 1;i < s.length();i++){

            //如果前后缀不相等,j就要循环回退直到j=0
            while(j > 0 && s.charAt(i) != s.charAt(j)){
                //next[j - 1]代表j前面的字符串的最长相同公共前后缀长度
                j = next[j - 1];  //next[j - 1]前的字符代表已经匹配上的
            }
            //如果前后缀相等
            if(s.charAt(i) == s.charAt(j)){
                j++; //j表示的是前缀末尾,其+1正好等于最长相同公共前后缀长度
            }
            next[i] = j; //更新next前缀表
        }

    }
}

总结

1.思想

(1)为什么用KMP算法?

        KMP主要用于解决的问题是,判断一个文本串中是否包含另一个模式串。这题就是KMP的景点题目。

(2)KMP的核心思想?

        如果用暴力解法,判断一个文本串中是否包含另一个模式串时,如果出现匹配失败时,会回跳到模式串的首字母重写匹配,时间复杂度是O(m*n)。而KMP通过计算了一个前缀表next,当出现匹配失败时,不会回跳到模式串的首字母,而是直接回跳到上一个匹配的子串后边继续匹配

        那为什么KMP可以出现不匹配的情况,可以直接定位到上次匹配的子串然后继续匹配呢?是因为它提前计算的一个next数组起了作用。

        那什么是next数组?其实,next数组是以模式串各个字符串为结尾的,每个子串的最长相等前后缀的集合。举个例子,假设s = aabaaf,next数组=010120,具体计算过程如下。

        s = a,没有前缀和后缀,next值=0

        s = aa,前缀=a,后缀=a,next值=1

        s = aab,没有相同的前缀和后缀,next值=0

        s = aaba,前缀=a,后缀=a,next值=1

        s = aabaa,前缀=aa,后缀=aa,next值=2

        s = aabaaf,没有相同的前缀和后缀,next值=0

        现在我们已经计算得到了next数组,那么next如何使用,帮助我们在出现不匹配的情况下回退到上一个匹配的位置呢?继续举例,假设文本串=aabaabaaf,模式串=aabaaf,使用过程如下。

        对于模式串的前五个字母aabaa,我们成功匹配。        

        对于模式串的第六个字母b≠f,出现不匹配了,那么我们希望模式串能够回退到上一次匹配的位置是b。为什么是b,看下图就很清楚了,由于b和f前面都匹配,所以A2=A3,又由于最长相同前后缀,所以A1=A2,因此A1=A3,即模式串的前两个字母aa已经匹配成功了,我们只需要从模式串的第三个字母b后面继续和文本串匹配就行。

        先说结论,让j回退的方法是让j等于其前一个下标的next数组值,即j=next[j-1]。为什么用这个公式就能当j回退到上一个匹配位置后边字母呢?next数组我们前面已经计算完成,如下图红色字迹。而字母f的前一个下标的next存储的是2,2代表f字符串前面的aabaa相同前缀A1的长度是2,正好b的下标就是2,所以当j指向f出现不匹配时,我们通过修改j=next[j-1],让j回退当上一个满足匹配的子串后边继续匹配。

        这样,当出现不匹配要回退时,关于next数组的使用方法,以及为什么可以通过next数组进行回退就说完了。

2.算法流程

(1)如何计算next数组

        前面原理我只说了手动计算next数组的方法,而代码要如何实现呢?

        主要分为三个步骤,初始化+前后缀不相同处理+前后缀相同处理。接下来我一步步说明每个步骤的流程。

        首先是初始化,我们令next[0] = 0,因为第一个字母没有相同前后缀。令j = 0,用j代表前缀的末尾字母的下标,因此j+1也就是前缀长度了。

        然后我们用i代表后缀末尾字母的下标,遍历模式串s,s的区间范围是[1,s.length()),这里的i从i开始,是因为next[0]已经初始化好了,后缀至少从index=1开始。

        注意,一定要记住我们用用j代表前缀的末尾字母的下标,用i代表后缀末尾字母的下标!!!

        接下来,我们会判断i和j下标指向的是不是相同的字母,根据前缀字母和后缀字母相同和不相同分别处理。

        如果前后缀i和j指向的字母相同,就让j++,i++。同时令next[i] = j,更新next数组。为什么要让j++呢?因为j指向的是前缀末尾的下标,其+1,就相当于前缀的长度,next[i] = j就让当前i的保存的相同前后缀长度等于j表示的前缀长度。然后i++,继续向模式串后面遍历。(此时j指向的是相同前缀字母的下一个位置,如果下一次字母比较的结果仍是相同,next[i+1]会等于j+1,即next[i]+1。)

        如果前后缀i和j指向的字母不相同,j就要循环回退直到j=0,回退的公式是j = next[j - 1],next[j - 1]代表j前面的字符串的最长相同公共前后缀长度。

        这样,关于next数组计算的流程就说完了,如果还是有点晕,可以把这里的算法流程和前面原理部分手算next数组的例子结合看,再理解一下。

(2)next如何使用

        这里说一下计算完next数组后,我们如何调用使用它。

        首先假设文本串是s,模式串是t。先处理两种特殊情况。如果模式串长度为0,说明模式串为0,直接return 0。如果模式串t的长度大于文本串s,肯定找不到匹配情况,直接return -1。

        接下来,我们初始化j=0,表示模式串的下标,初始化i=0,表示文本串的下标。令i遍历for循环遍历文本串s,进行s[i]和t[j]的判断。

        如果s[i] != t[j],说明文本串的字符和模式串的字符不相等,即出现冲突了,j要循环回退,回退公式是 j = next[j - 1]。

        如果s[i] == t[j],说明当文本串的字符和模式串的字符相等,让i++,j++,继续向后遍历。

        然后,需要判断模式串是否已经成功匹配到最后,如果j == t.length()说明匹配成成功了,直接返回匹配下标i - t.length() + 1。

        最后,如果for循环结束,还没有return,就说明s中不包含模式串t,我们return -1即可。         

3.注意点

(1)计算next数组是,如果前后缀不相等,j要回退,这里用的是while循环回退。同理,判断s[i]和t[j]如果字母不相同,j也是while循环回退。这里其实我没有理解的很明白,就是先记住它吧!

(2)while循环回退时,有一个条件是j > 0,因为j=0是已经指向模式串首字母了,不用再回退了。

(3)模式串已经成功匹配到最后,直接返回匹配下标i - t.length() + 1,这里如果不确定举个例子就行。

459.重复的子字符串

题目

        给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

示例 1:

        输入: "abab"

        输出: True

        解释: 可由子字符串 "ab" 重复两次构成。

示例 2:

        输入: "aba"

        输出: False

代码

class Solution {
    public boolean repeatedSubstringPattern(String s) {

        //先构造s+s字符串
        StringBuilder sb = new StringBuilder(s + s);

        //去头部和尾部字符
        sb.deleteCharAt(0);
        sb.deleteCharAt(sb.length() - 1);

        //如果去头去尾的sb仍然能匹配出s,就是重复字符串
        if(sb.indexOf(s) == -1){
            return false;
        }
        else{
            return true;
        }
    }
}

总结

1.思想

        题目要求判断一个字符串是不是由重复的子字符串构成的,如s=abababab就是由ab重复构成的。那么如何判断s是否由重复子串组成?答案是,如果s+s拼接后(去头去尾)的字符串中,仍然能包含s,就说明是由重复子串构成。

        这里举两个例子。

        s = abab,s+s = ababababa,去头去尾字符串bababab仍然包含ab,就是重复子串

        s = aba,s+s = abaaba,去头去尾字符串baab中,不包含ab,就不是重复子串

2.算法流程

        首先,创建一个StringBuild对象sb等于s+s,然后执行去头去尾操作,调用deleteCharAt()函数,分别删除索引=0和索引=sb.length()-1的元素。最后,用indexOf函数,判断sb字符串中是否包含s,如果不包含,函数返回索引值是-1,return false,否则就 return true。

3.语法问题

(1)StringBuild如何删除某个index的元素:sb.deleteCharAt(index)

(2)StringBuild如何判断是否包含某个子串并返回第一个匹配的位置:sb.indexOf(s)

4.注意点

        这里我偷懒了,没有用KMP手撕,直接调用了indexOf进行模式串匹配。(我太懒了,KMP太复杂了对我而言)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

货币与利率

货币与利率 货币及其职能什么是货币货币的职能货币带来了什么&#xff1f; 货币形式的演变商品货币代用货币信用货币货币的特性 现代社会货币的表现形式流通中的现金支票存款信用卡储存存款 货币层次划分目的划分标准划分种类我国的货币层次 货币与物价的关系利率什么是利息什么…

算法学习——LeetCode力扣补充篇1

算法学习——LeetCode力扣补充篇1 1365. 有多少小于当前数字的数字 1365. 有多少小于当前数字的数字 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个数组 nums&#xff0c;对于其中每个元素 nums[i]&#xff0c;请你统计数组中比它小的所有数字的数目。 换而言之&a…

推荐一本牛逼的入门 Python书!,如何试出一个Python开发者真正的水平

本书详细解说了 Python 语言和编程的本质&#xff0c;无论你是否接触过编程语言&#xff0c;只要是 Python 编程的初学者&#xff0c;都可阅读本书。 本书讲解的内容虽然基础&#xff0c;但并不简单。本书提供了 165 幅图表&#xff0c;可以让大家能够轻松地理解并掌握复杂的概…

Taro+vue3 监听当前的页面滚动的距离

1.需求 想实现一个这样的效果 一开始这个城市组件 是透明的 在顶部 的固定定位 当屏幕滑动的时候到一定的距离 将这个固定的盒子 背景颜色变成白色 2.Taro中的滚动 Taro中的滚动 有固定的api 像生命周期一样 这个生命周期是 usePageScroll import Taro, { useDidShow, useP…

外包干了5天,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

Django源码之路由的本质(上)——逐步剖析底层执行流程

目录 1. 前言 2. 路由定义 3. 路由定义整体源码分析 3.1 partial实现path函数调用 3.2 图解_path函数 3.3 最终 4.URLPattern和Pattern的简单解析 5. 小结 1. 前言 在学习Django框架的时候&#xff0c;我们大多时候都只会使用如何去开发项目&#xff0c;对其实现流程并…

【C++】位图

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;手撕哈希表的闭散列和开散列 > 毒鸡汤&#xff1a; 坚持不懈&#xff0c;才能在困难面前看到光明的希望。 > 专栏选自&#xff1a;C嘎嘎进阶 >…

基于单片机病房温度监测与呼叫系统设计

**单片机设计介绍&#xff0c;基于单片机病房温度监测与呼叫系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机病房温度监测与呼叫系统设计概要主要涵盖了通过单片机技术实现病房温度的实时监测以及病人呼叫功能…

java字符串(一)-- 字符串API,StringBuffer 和 StringBuilder,Object

String字符串相关的类 String的特性 String类&#xff1a;代表字符串。Java 程序中的所有字符串字面值&#xff08;如"abc" &#xff09;都作为此类的实例实现。String类是引用数据类型。 在 Java 8 中&#xff0c;String 内部使用 char 数组存储数据。 public fi…

C++入门知识点

目录 一、命名空间 1.命名空间的定义 2.命名空间的使用 二、输入和输出 三、缺省参数 1.缺省参数的概念 2.缺省参数的分类 1&#xff09;全缺省参数 2&#xff09;半缺省参数 四、函数重载 五、引用 1.引用的概念 2.引用的特性 3.引用和指针的区别 六、内联函…

gan zoo: 最新GAN 相关paper/code收集

相关推荐&#xff1a; 简单实现 GAN 简单实现 DCGAN 简单实现 InfoGAN 简单实现 Pix2Pix 一文带你读懂概率生成模型 GPT-1/GPT-2/GPT-3简介 GPT从0到1构建(附视频代码链接) 一文带你读懂变分自编码器(VAEs) 文本引导图像生成模型的演变(DALLE/CLIP/GLIDE) 作者对迄今为止所有的…

YOLOv7--复现并训练自己的数据集(简单)

目录 1、官网下载代码 2、上传代码至服务器 3、配置环境 4、上传数据集 5、新建yaml文件 6、修改Yolov7.yaml文件 7、修改train.py文件 8、开始训练 9、复现Yolov7遇到的错误&#xff1a; 1、官网下载代码 Yolov7代码下载网址&#xff1a; https://github.com/WongKi…

【vue2+antvx6】报错Cannot read properties of undefined (reading ‘toUpperCase‘)

我的代码是这样的 <el-collapseref"collapse"v-model"active"accordionclass"collapseStart"change"collapsechange"><el-collapse-item:name"String(index 1)"v-for"(i, index) in List":key"in…

Linux重点思考(下)--shell脚本使用以及内核开发

Linux重点思考(下&#xff09;--shell脚本使用和组合拳 shell脚本的基础算法shell脚本写123...n的值&#xff0c;说思路Shell 脚本用于执行服务器性能测试的死循环Shell 脚本备份和定时清理垃圾文件 shell脚本的内核开发正向映射反向映射 shell脚本的基础算法 shell脚本写123……

SpringBoot常见注解有哪些

Spring Boot的核心注解是SpringBootApplication , 他由几个注解组成 : ● SpringBootConfiguration&#xff1a; 组合了- Configuration注解&#xff0c;实现配置文件的功能&#xff1b; ● EnableAutoConfiguration&#xff1a;打开自动配置的功能&#xff0c;也可以关闭某个自…

答辩PPT最后一页除了写谢谢观看,谢谢聆听,感谢老师,还可以写什么呢?

以我的答辩PPT为例吧 我当时写的这两句话得到了导师的肯定&#xff0c;大家可以参考 感谢导师悉心指导&#xff0c;敬请老师批评指正

linux之忘记root密码

一&#xff0c;开机到如下地方按下e进入紧急模式 然后再如下位置输入init/bin/bash 然后Ctrlx 二&#xff0c; 修改密码 以上操作分别为 1&#xff09;&#xff0c;重新挂载根目录 mount -o remount,rw / 2&#xff09;&#xff0c;修改密码 passwd root 3&#xff09;&a…

redis-shake可视化监控

目录 一.redis-shake v4 1.镜像 2.shake.toml 3.启动redis-shake后 二.json-exporter配置 1.Dockerfile 2.config.yml 三.prometheus配置 1.prometheus.yml 2.redis-shake.json 四.grafana 一.redis-shake v4 1.镜像 ######################### Dockerfile #########…

平衡二叉树(AVL树)

文章目录 平衡二叉树&#xff08;AVL树&#xff09;1、平衡二叉树概念2、平衡二叉树的的实现2.1、平衡二叉树的结点定义2.2、平衡二叉树的插入2.3、平衡二叉树的旋转2.3.1、右单旋&#xff08;R旋转&#xff09;2.3.2、左单旋&#xff08;L旋转&#xff09;2.3.3、先右单旋再左…

详解JAVA程序调优

目录 1.概述 2.命令 2.1.查看JAVA进程 2.2.查看虚拟机状态 2.3.查看线程的情况 3.工具 3.1.jconsole 3.2.jvisualVM 4.实战场景 1.概述 在实际工作中我们难免会遇见程序执行慢、线程死锁等一系列的问题&#xff0c;这时候就需要我们定位具体问题然后来解决问题了。所…