leetcode 字符串篇

344. 反转字符串https://leetcode.cn/problems/reverse-string/

太简单了一些……

时间复杂度:O(n)

空间复杂度:O(1)

    let l = 0;
    let r = s.length-1;
    while(l<r){
        let tmp = s[l];
        s[l] = s[r];
        s[r] = tmp;
        l++;
        r--;
    }
    return s;

541. 反转字符串 IIhttps://leetcode.cn/problems/reverse-string-ii/

【一些同学可能为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。】——我就是这么做,自己就晕了,还没搞出来!!!😭

其实在遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。

时间复杂度:O(n)

空间复杂度:O(1) 或 O(n),取决于使用的语言中字符串类型的性质。如果字符串是可修改的,那么我们可以直接在原字符串上修改,空间复杂度为 O(1),否则需要使用 O(n) 的空间将字符串临时转换为可以修改的数据结构(例如数组),空间复杂度为 O(n)。------来自官方的解释~

s = s.split("");
let len = s.length;
for(let i = 0;i<len;i+=(2*k)){
    // 确定区间
    let l = i-1;
    let r = i+k > len ? len : i+k;
    // 确定边界
    l++;
    r--;
    // 开始交换
    while(l<r){
        let tmp = s[l];
        s[l] = s[r];
        s[r] = tmp;
        l++;
        r--;
    }

}
return s.join("");

剑指 Offer 05. 替换空格https://leetcode.cn/problems/ti-huan-kong-ge-lcof/

一个简单的方法:

s = s.replaceAll(" ","%20");
return s;

我一直在想下面直接替换为啥不行,一想好像也没有这么简单。

for(let i = 0;i<s.length;i++){

if(s[i] === " "){

s[i] = "%20";

}

}

百度了之后才知道“JS中字符串不能直接 str[i] = 'x',应该转化成数组再去操作,这才是想考的点吧……

  // 那我就先转化成数组
    s = s.split("");
    for(let i = 0;i<s.length;i++){
        if(s[i] === " "){
            s[i] = "%20";
        }
    }

151. 反转字符串中的单词https://leetcode.cn/problems/reverse-words-in-a-string/

使用 split 将字符串按空格分割成字符串数组;咋没有想到用空格分割!!!一直用“”思维都固定了

使用 reverse 将字符串数组进行反转;

使用 join 方法将字符串数组拼成一个字符串。

直接用三个方法,原字符串中前面后面如果有空格,中间有多个空格,结果字符串中还是会有的

str.trim()可以去除字符串的首尾空格

 \s表示:匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。

+表示:匹配前面的子表达式一次或多次

我自己想不到用\s+ 意思就是匹配任何字符一次,从而去除字符之间的多余空格,这里的意思就是匹配一个空格进行分隔字符。和用trim(),该方法是去除字符串前后多余的空格。

return  s.trim().split(/\s+/).reverse().join(" "); 

自己的想法一直没通过,需要好好琢磨一下,要理解意思~

剑指 Offer 58 - II. 左旋转字符串icon-default.png?t=M85Bhttps://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/

最直接的想法就是将前n个先插入结果数组,将剩余的数从头部插入结果数组。

时间复杂度:O(n)

空间复杂度:O(n)

    let ans = [];
    let count = 0;
    for(let i = 0;i<s.length;i++){
        count++;
        if(count === n){
            ans.push(s.slice(0,n));
        }
    }
    ans.unshift(s.slice(n));
    return ans.join("");

学习到另外一种解法,先反转前n个字符,再反转后n个字符,再将整个字符进行反转。

28. 找出字符串中第一个匹配项的下标icon-default.png?t=M85Bhttps://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/

一看到找下标,我就想到了indexOf()方法,果然很nice,一行解决。

 return haystack.indexOf(needle);

就是学过的字符串匹配,但是忘记了,KMP算法

方法二:暴力匹配,直接遍历和比较每个组成的子串

时间复杂度:O(n*m) n是第一个字符串的长度 m是第二个字符串的长度

空间复杂度:O(1)

   // i条件:从哪个下标开始的子串长度仍要小于haystack的长度
    for(let i = 0; i + needle.length<=haystack.length;i++){
        let flag = true;
        for(let j = 0;j<needle.length;j++){
            // i+j不是j,是因为对于haystack.length来说,此时的下标并不是从0开始的,
            // 而是从i+j开始的。i=0是第一个比较的子串的起始位置,之后子串的下标就是i+j
            if(haystack[i+j]!==needle[j]){
                flag = false;
                break;
            }
        }
        if(flag){
            return i;
        }

    }
   return -1;

方法三:KMP算法

通过代码随想录的视频,一看就懂!!帮你把KMP算法学个通透!(理论篇)_哔哩哔哩_bilibili

应该就是:利用已经部分匹配的有效信息,保持i指针不回溯,修改j指针,让模式串移动到最长相等前后缀的位置。

next[i] 表示 i(包括i)之前最长相等的前后缀长度。

时间复杂度:O(n)

空间复杂度:O(n)

 //kmp算法:前缀表不减1的情况
    if(needle.length === 0){
        return -1;
    }
    //定义获取next数组的函数
    const getNext = (needle) => {
        let next = [];
        let j = 0;
        next.push(j);
        for(let i = 1;i<needle.length;i++){
            while(j>0 && needle[j]!=needle[i]){
                j = next[j-1];
            }
            if(needle[j] === needle[i]){
                j++;
            }
            next.push(j);
        }
        return next;
    }
    //获取next数组
    let next = getNext(needle);
    //进行匹配,i不回溯
    let j = 0;
    for(let i = 0;i<haystack.length;i++){
        while(j>0 && haystack[i] !== needle[j]){
            j = next[j-1];
        }
        if(haystack[i] === needle[j]){
            j++;
        }
        if(j === needle.length){
            return i-needle.length+1;
        }
    }
    return -1;

459. 重复的子字符串icon-default.png?t=M85Bhttps://leetcode.cn/problems/repeated-substring-pattern/

第一次的想法没有通过,错在找子串的时候,默认是子串中的字符都不想等的,其实是可以的,比如子串为aba。

看完题解,有个关键点:因为子串至少需要重复一次,所以子串不会大于一半

方法一:利用方法:s.repeat(x) 方法字符串复制指定x次。

时间复杂度:O(n)

空间复杂度:O(n)

  let n = s.length;
    if(n < 2){
        return false;
    }
    let str = "";
    let middle = Math.floor(n/2);
    for(let i = 0;i<middle;i++){
        str += s[i];
        if(s === str.repeat(Math.floor(n/str.length))){
            return true;
        }
    }
    return false;

方法二:kmp算法的next数组

看了https://programmercarl.com/0459.%E9%87%8D%E5%A4%8D%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2.html#kmp

的解析,其实我有个疑惑,只要最长相等前后缀的长度能被s的长度整除不就代表有重复的子串吗,为啥还需要用s的长度%(s的长度-最长相等前后缀的长度),就是if (next[next.length - 1] !== 0 && s.length % next[next.length - 1] === 0),其实是错的,比如aba,最长相等前后缀长度是1,s.length = 3;3%1===0,但aba不符合条件。

虽然我明知错的但是我还是不是很理解……好吧

时间复杂度:O(n)

空间复杂度:O(n)

(下面的next数组也是不减一的)

  const getNext = (s) => {
        let next = [];
        let j = 0;

        next.push(j);

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

        return next;
    }

    let next = getNext(s);
    
    if (next[next.length - 1] !== -1 && s.length % (s.length - (next[next.length - 1] + 1)) === 0)
        return true;
    return false;

字符串篇结束啦,但是仍需要细细琢磨和努力,加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值