leetcode刷题之字符串相关问题

344.反转字符串

leetcode刷题之字符串相关问题_第1张图片

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

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

leetcode刷题之字符串相关问题_第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 替换空格

leetcode刷题之字符串相关问题_第3张图片

方法一: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.反转字符串中的单词

leetcode刷题之字符串相关问题_第4张图片

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

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

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

leetcode刷题之字符串相关问题_第5张图片

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

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.找出字符串中第一个匹配项的下标

leetcode刷题之字符串相关问题_第6张图片
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)
};

你可能感兴趣的:(Leetcode刷题,and,Js,leetcode,算法,职场和发展)