leetcode刷题之字符串相关问题

news2024/9/20 14:22:37

344.反转字符串

在这里插入图片描述

方法一:找中间结点,头尾翻转

var reverseString = function(s) {
    let len = s.length
    let mid = Math.floor((s.length - 1) / 2)  //向下取整 如果长度是奇数,那么mid是最中间的结点 如果长度是偶数,那么mid是中间两个结点的前一个
    console.log(len,mid)
    for(let i=0;i<=mid;i++){
        [s[i],s[len - 1 - i]] = [s[len - 1 - i],s[i]]
    }
};

方法二:使用left right指针

var reverseString = function(s) {
    let len = s.length
    if(len==0||len==1) return s
    let left = 0,right = len - 1
    while(left<right){
        [s[left],s[right]] = [s[right],s[left]]
        left++
        right--
    }
};

541.翻转字符串2

在这里插入图片描述

方法一:递归

var reverseStr = function(s, k) {
    let arr = s.split("")
    const helper = (a,b) =>{
        //反转实现的具体功能
        let left = a,right = b
        while(left<right){
            [arr[left],arr[right]] = [arr[right],arr[left]]
            left++
            right--
        }
    }

    let len = s.length
    const reduce = (start,end) =>{
        if(start>end) return
        if(end - start < k){
            helper(start,end)
            return 
        }
        if(end - start < 2*k){
            helper(start,start+k-1)
            return 
        }
        //翻转
        helper(start,start+k-1)
        //继续递归
        reduce(start+2*k,len-1)
    }
    reduce(0,len - 1)
    return arr.join("")
};

方法二:迭代

var reverseStr = function(s, k) {
    const helper = (start,end) =>{
        while(start<end){
            [arr[start],arr[end]] = [arr[end],arr[start]]
            start++
            end--
        }
    }

    let len = s.length
    let arr = s.split("")
    for(let i=0;i<len;i+=2*k){
        if(i+k<=len){
            helper(i,i+k-1)
        }else{
            helper(i,i+len-1)
        }
    }
    return arr.join("")
};

剑指offer05 替换空格

在这里插入图片描述

方法一:split join

var replaceSpace = function(s) {
    //js中字符串不支持修改
    let str = s.split(" ").join("%20")
    return str
}

方法二:replaceAll

var replaceSpace = function(s) {
    //js中字符串不支持修改
    return s.replaceAll(" ","%20")
}

方法三:拼接新的字符串

var replaceSpace = function(s) {
    //js中字符串不支持修改
    let str = ''
    for(let i=0;i<s.length;i++){
        str+=s[i]==" "?"%20":s[i]
    }
    return str
}

方法四:手动操作

参考

var replaceSpace = function(s) {
    //js中字符串不支持修改
    //原地修改 不借助任何js内置函数
    let arr = s.split("")
    let oldLen = arr.length
    let spaceCount = 0 //用来记录空格的个数
    for(let item of arr){
        if(item==" ") spaceCount++
    }
    let newLen = oldLen + spaceCount*2  //注意这里不是乘以3 因为原来的空格占一个位置,所以这里用来替换空格 每个加2
    let i = oldLen - 1 , j = newLen - 1
    //思路是 每次将字符串往后移动
    for(;i<j;i--,j--){
        if(arr[i]!==" "){
            arr[j] = arr[i]
        }else{
            arr[j] = '0'
            arr[j-1] = '2'
            arr[j-2] = '%'
            j-=2  //这里虽然j移动了三个位置,for循环里面j会减一
        }
    }
    return arr.join("")
}

151.反转字符串中的单词

在这里插入图片描述

方法一:纯手动,去多余空格+整体反转+单词翻转

这是按照代码随想录的写法,主要锻炼思维能力

var reverseWords = function(s) {
    //思路:将字符串转数组 然后去掉多余的空格 然后翻转 
    let arr = Array.from(s)

    //去掉多余的空格
    const moveExtraSpace = arr =>{
        //使用快慢指针
        let slow = 0, fast = 0
        while(fast<arr.length){
            //当fast指向的位置是空格,且是第一个位置 那么fast向后移动 或者 别的情况出现空格表示一个单词的结束 我们需要保留一个空格 连续出现两个空格 就将多余的消除掉
            if(arr[fast]==" "&&(fast==0||arr[fast-1]==" ")){
                fast++
            }else{
                arr[slow++] = arr[fast++]
            }
        }
        //while循环结束之后 fast指向arr的最后一个位置 
        //去掉末尾的空格 如果arr最后几个位置都是空格 那么最后可能的情况是slow不动了(此时指向空格的下一个位置) fast一直移动直到跳出while      => 这里要判断arr[slow-1]
        arr.length = arr[slow-1] == ' '?slow-1:slow
    }

    const reverseArr = (arr,start,end) =>{
        let left = start,right = end
        while(left<right){
            [arr[left],arr[right]] = [arr[right],arr[left]]
            left++
            right--
        }
    }


    moveExtraSpace(arr)
    reverseArr(arr,0,arr.length-1)

    //对每个单词进行翻转
    let start = 0
    for(let i=0;i<=arr.length;i++){
        if(arr[i]==" " || i == arr.length){  //这里当i指向最后一个位置的下一个位置的时候,需要对最后一个单词进行翻转
            //对每个单词进行翻转  当i指向空的时候,表示一个单词的结束 我们让start指向i的下一个位置 指向新单词的开始
            reverseArr(arr,start,i-1)
            start = i + 1
        }
    }

    return arr.join("")

};

方法二:正则表达式

split(/\s+/) 是 JavaScript 中用于对字符串进行分割的方法,其中 /\s+/ 是一个正则表达式,表示匹配一个或多个空格字符(包括空格、制表符和换行符)。
例如,如果有一个字符串 “hello world”,执行该字符串的 split(/\s+/) 方法会将其分割成两个部分:[“hello”, “world”]。也就是说,字符串中的空格被用作分隔符,返回一个数组,数组中的元素为被分割后的各个部分。

var reverseWords = function(s) {
    return s.trim().split(/\s+/).reverse().join(" ")
};

方法三:按照自己的想法来

var reverseWords = function(s) {
    //自己的思路
    s = s.trim()
    let newStr = ""
    for(let i = 0;i<s.length;i++){
        if(s[i]==" "&&s[i-1]==" "){  //s已经去掉了首尾的空格 那么i=0的时候就不会出现 空格
            continue//进入下一层循环
        }else{
            newStr+=s[i]
        }
    }
    return newStr.split(" ").reverse().join(" ")
};

剑指offer - 左旋转字符串2

在这里插入图片描述

方法一:直接在数组后面填,暴力

var reverseLeftWords = function(s, n) {
    if(n>=s.length) return s
    let arr = s.split("")
    let cur = s.length
    let count = 0
    let i = 0
    while(count<n){
        arr[cur] = arr[i]
        cur++
        i++
        count++
    }
    return arr.splice(n).join("")
};

方法二:局部翻转+整体翻转

var reverseLeftWords = function(s, n) {
    //翻转字符串
    const reverse = (str,left,right) =>{
        let arr = str.split("")
        while(left<right){
            [arr[left],arr[right]] = [arr[right],arr[left]]
            left++
            right--
        }
        return arr.join("")
    }
    //先两部分局部翻转 然后整体反转
    s = reverse(s,0,n-1)
    s = reverse(s,n,s.length-1)
    return reverse(s,0,s.length-1)
};

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

在这里插入图片描述
haystack是文本串(设其长度是n),needle是模式串(设其长度是m)

使用最原始的字符串的匹配,超时

时间复杂度是O(mn)

var strStr = function(haystack, needle) {
    let i = 0 , j = 0
    while(i<haystack.length&&j<needle.length){
        if(haystack[i]==needle[j]){
            i++
            j++
            //如果j++之后已经溢出了,那么说明needle已经判断完了 
            if(j==needle.length) return i - j 
            continue  //进入下一层循环
        }else{
            j = 0
        }
    }
    return -1
};

js内置函数indexof

var strStr = function(haystack, needle) {
    return haystack.indexOf(needle)
};

kmp算法

时间复杂度是O(n+m)

next数组保留原始的数值 实现方法一

var strStr = function(haystack, needle) {
    //kmp算法 ①要知道最长公共前后缀的长度;②求next数组,利用next数组进行跳转,当模式串不匹配的时候,去查找相应的next数组,next数组 下标为i的地方 表示i之前的(包含i)的字符串的最长公共前后缀的长度;next数组有两种表示方式,第一种 整体减一 第二种 保留原数
    //第一种,next整体保留原样

    if(needle.length==0) return 0
    //根据模式串来求next数组
    const getNext = str =>{   //这种方式 前缀必须得有第一个元素 后缀必须得有最后一个元素 i指向当前(包括i这个位置)这样一个串的最大公共前后缀的长度 
        //也可以将求next数组的过程中,next[i]放的是指针i之前(包括i的)这样一个字串
        let next = []
        let j = 0 //j 指向字符串前缀的末尾
        next.push(j)

        for(let i=1;i<str.length;i++){
            //由于i在后缀上 j在前缀上 当i j两个位置的元素不匹配的时候,j需要next数组来找j-1前面的串的最大前后缀的公共长度
            while(j>0&&str[i]!==str[j]){
                j = next[j-1]   //j进行回退
            }
            if(str[i]==str[j]){
                j++    //i就不用加了 for里面
            }
            next[i] = j
        }
        return next
    }

    //进行模式串的匹配
    let j = 0 //指向模式串
    //对文本串进行遍历
    let next = getNext(needle)
    for(let i=0;i<haystack.length;i++){
        while(j>0&&haystack[i]!==needle[j]){
            //这种情况j要根据next数据进行回退
            j = next[j-1]
        }
        if(haystack[i]==needle[j]){
            j++
        }
        if(j==needle.length){
            return i-needle.length+1
        }
    }
    return -1
    
};




var strStr = function(haystack, needle) {
    //实现方法二
    if(needle.length==0) return 0

    const getNext = str =>{
        let next = []
        let j = -1   //整体的next数组中的值 减一
        next.push(j)
        for(let i=1;i<str.length;i++){
            while(j>=0&&str[i]!==str[j+1]){
                j = next[j]
            }
            if(str[i]==str[j+1]){
                j++
            }
            next.push(j)
        }
        return next
    }

    let next = getNext(needle)
    let j = -1
    for(let i=0;i<haystack.length;i++){
        while(j>=0&&haystack[i]!==needle[j+1]){
            j = next[j]
        }
        if(haystack[i]==needle[j+1]){
            j++
        }
        if(j==needle.length-1){
            return i-needle.length+1
        }
    }
    return -1

};

459.重复的子字符串

方法一:暴力解法 类似滑动窗口

var repeatedSubstringPattern = function(s) {
    //滑动窗口 
    let mid = Math.ceil((s.length-1)/2)
    for(let i=1;i<=mid;i++){
        let temp = s.slice(0,i) //temp作为窗口
        //这个窗口从 s的 i这个位置开始进行比较
        let j = 0 //指向temp的起始位置
        let cur = i //cur指向s的i的位置
        while(cur<s.length){
            if(s[cur]==temp[j]){
                cur++
                j++
            }else{ //continue 会进入下一趟的while循环 break跳出
                break  //进入下一层for循环  
            }
            if(cur<s.length&&j>=temp.length){ //只有当s还没有比较完成
                j = 0
            }
        }
        //s temp比较完成最后一个 cur++ j++ 
        if(cur==s.length&&j==temp.length) return true
    }
    return false
};

方法二:kmp算法

时间复杂度是O(n) 求next数组
空间复杂度是O(n) next的大小

var repeatedSubstringPattern = function(s) {
    //使用kmp算法 求next数组 整个字符串s的最长公共前后缀的长度
    //比如字符串                ababababab
    // 那么最长的前缀字符串是    abababab
    //最长的后缀字符串是           abababab   这样我们的重复的字串是 ab 整个串s的长度对ab长度进行整除 正好可以整除

    const getNext = str =>{
        let j = 0
        let next = []
        next.push(j)
        for(let i=1;i<str.length;i++){
            while(j>0&&str[i]!==str[j]){
                j = next[j-1]
            }
            if(str[i]==str[j]){
                j++
            }
            next[i] = j
        }
        return next
    }

    let next = getNext(s)
    //判断s的公共前后缀长度是否是0
    if(next[s.length-1]!==0&&(s.length%(s.length-next[s.length-1]))==0) return true
    return false
};

方法三:移动匹配

var repeatedSubstringPattern = function(s) {
    //移动匹配 s+s之后,去掉头尾得到t 那么t中一定有s这个串 为啥要去头尾呢?避免头尾的影响
    let t = s + s
    t = t.slice(1,2*s.length - 1)
    return t.indexOf(s) == -1?false:true  //这里要注意:使用库函数判断是否包含一个串 时间复杂度是O(m+n) 如果要暴力的话,时间复杂度O(mn)
};

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

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

相关文章

【Java技术专题】「攻破技术盲区」带你攻破你很可能存在的Java技术盲点之动态性技术原理指南(反射技术专题)

带你攻破你很可能存在的Java技术盲点之动态性技术原理指南 带你攻破你很可能存在的Java技术盲点之动态性技术原理指南编程语言的类型静态类型语言动态类型语言 技术核心方向反射API反射案例介绍反射功能操作获取构造器长度可变的参数 - 构造方法使用反射 API 获取参数长度可变的…

【软件测试】

系列文章目录 文章目录 系列文章目录前言第四章 单元测试4.1 软件测试过程概述4.2 什么是单元测试4.2.1 单元测试的定义4.2.2 单元测试的重要性4.2.3 单元测试原则 4.3 单元测试的目标和任务4.3.1 单元测试的目标&#xff1a;单元模块被正确编码4.3.2 单元测试的主要任务 4.4 单…

FreeRTOS:事件标志组

目录 一、事件标志组简介1.1事件位(事件标志)1.2事件组1.3事件标志组和事件位的数据类型 二、创建事件标志组2.1函数 xEventGroupCreate()2.2函数xEventGroupCreateStatic() 三、设置事件位3.1函数 xEventGroupClearBits()3.2函数xEventGroupClearBitsFromISR()3.3函数 xEventG…

Python模块MarkupPy 自定义html报告

简介 MarkupPy是Python模块用于生成HTML和XML格式的字符串。它的主要作用是提供了一种比原生HTML/XML更加易读和易写的编写方式&#xff0c;通过Python代码来生成HTML或XML代码。 使用MarkupPy&#xff0c;可以在Python中使用不同的对象类型和方法&#xff0c;来动态地生成HTML…

做自动化测试老是失败?你真的会做吗?资深测试的总结整理...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化正在不断普…

揭开生成式人工智能的力量:60+医疗保健应用场景

预计生成式AI在医疗保健领域的增长速度将超过任何其他行业。在医疗技术领域&#xff0c;AI可带来更高效流程、个性化客户互动、更大的创新和更高价值。为了帮助领导者理解这些机会&#xff0c;BCG最近研究了医疗技术中生成式AI的60多个应用场景&#xff1a;从产研和软件开发到业…

Android自动化测试,5个必备的测试框架

Appium Appium是一个开源的移动测试工具&#xff0c;支持iOS和Android&#xff0c;它可以用来测试任何类型的移动应用&#xff08;原生、网络和混合&#xff09;。作为一个跨平台的工具&#xff0c;你可以在不同的平台上运行相同的测试。为了实现跨平台的功能&#xff0c;Appi…

关于数据库索引的入门简述

一、简介 数据库索引是现代数据库中高效数据检索的一个重要工具。它在优化查询性能和加快数据检索操作方面发挥着重要作用。这里我们深入了解下数据库索引其内部工作原理、优点和局限性。 二、数据库 1、SQL 数据库 为了理解索引&#xff0c;先说一句数据库&#xff0c;数据库…

Jenkins小技巧汇总

设置变量 设置全局环境变量 全局变量除了系统内置的全局环境变量之外&#xff0c;用户也可以设置全局变量。设置路径&#xff1a;【Dashboard】–>【Manage Jenkins】–>【System Configuration 下的 System】–>【Global properties】从描述中我们可以看到&#xf…

Jetson nano 之 ROS入门 - - 深度学习环境配置

文章目录 前言一、Anaconda安装二、Pytorch 与 TensorFlow 环境配置三、TensorRT 推理引擎配置总结 前言 Jetson Nano是一款由NVIDIA推出的小型计算机&#xff0c;其性能优异、功耗低、体积小巧&#xff0c;非常适合用于嵌入式系统和边缘设备的深度学习应用。Jetson Nano搭载了…

【Git原理与使用】-- 基本操作

目录 添加文件 查看objects中的文件 小结 修改文件 版本回退 回退的回退 小结 撤销修改 情况一&#xff1a;对于工作区的代码&#xff0c;还没有 add 情况二&#xff1a;已经 add &#xff0c;但没有 commit 情况三&#xff1a;已经 add &#xff0c;并且也 commit …

Cordic IP核使用说明以及避坑记录

Cordic IP核使用说明以及避坑记录 参考文章&#xff1a;(140条消息) Vivado cordic IP核rotate和translate使用详解(附有代码)_cordic ip核 rotate_迎风打盹儿的博客-CSDN博客 (140条消息) VIVADO cordic IP核_卡布奇诺加勺糖的博客-CSDN博客 文章目录 Cordic IP核使用说明以及…

面试题:推排序是一种稳定排序吗?

面试题&#xff1a;推排序是一种稳定排序吗&#xff1f; 在回答该问题前&#xff0c;首先需要了解什么是稳定排序。 稳定性就是指对于两个关键字相等的记录&#xff0c;它们在序列中的相对位置&#xff0c;在排序之前和排序之后没有发生改变。通俗地讲就是有两个关键字相等的…

Node.js---菜鸟教程

文章目录 创建第一个应用创建 Node.js 应用 NPM 使用介绍使用 npm 命令安装模块本地安装使用 package.json模块的操作 回调函数阻塞代码实例非阻塞代码 事件循环事件驱动程序 EventEmitterEventEmitter 类方法实例error 事件继承 EventEmitter Buffer&#xff08;缓冲区&#x…

Redis-缓存

新增或者更新数据时,创建以后顺便存到redis中去【维护缓存】 获取的时候先从redis缓存中拿数据 如果拿数据的时候为空,则到数据库中拿数据,后再存到redis缓存中去 大量的商品【包括冷门商品】都进行上面的缓存,那么就很耗内存 针对每个数据进行缓存的时候 维护一个过期时间…

MQTT(一)

MQTT&#xff08;一&#xff09; 1.背景 学习目标&#xff1a;经过了解&#xff0c;Netty占用服务器资源内存大、远距离传感器在极端条件下数据处理兼容较差&#xff08;网络条件差&#xff0c;需要反复重连等&#xff09;。从同行业了解到&#xff0c;现在主流工业传输使用M…

【博学谷学习记录】超强总结,用心分享 | 架构师 zabbix学习总结

文章目录 一、介绍zabbix zabbix专有词汇 二、zabbix zabbix实践修改zabbix zabbix语⾔服务器可视化指标解决zabbix zabbix乱码问题查看监控内容可视化监控agent agent的cpu cpu动态查看模板--监控项⾃定义监控项语法 一、介绍 Zabbix 是由 Alexei Vladishev 开发的⼀种⽹络监…

达梦数据库介绍

文章目录 前言一、达梦数据库的定位二、达梦有哪些工具1、达梦管理工具2、达梦数据迁移工具3、 达梦数据库配置助手4、其它工具 三、Linux下的工具1、数据库初始化工具2、数据库迁移工具3、其它工具 四、其它连接工具总结 前言 近几年由于各种原因&#xff0c;国内开启了一波国…

【Android】-- 如何对APP版本控制/更新?

目录 一、 前提准备 1、获取服务器 2、使用工具操作云服务器 二、Json格式网页 三、创建file_paths.xml及修改AndroidManifest.xml 四、在java代码加入更新检测代码 效果如图&#xff1a; 可以强制更新和非强制更新&#xff0c;和浏览器下载安装包。 一、 前提准备 1、获取…

0001-TIPS-2020-hxp-kernel-rop : ret2user

目的 理解系统调用的过程&#xff1a;从用户态进入内核态&#xff0c;再从内核态返回用户态。细节见文末的参考了解一般性提权方法commit_creds(prepare_kernel_cred (0)); 环境搭建 下载 pwn 2020-kernel-rop wget https://2020.ctf.link/assets/files/kernel-rop-bf9c106…