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. 左旋转字符串https://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. 找出字符串中第一个匹配项的下标https://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. 重复的子字符串https://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数组
的解析,其实我有个疑惑,只要最长相等前后缀的长度能被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;
字符串篇结束啦,但是仍需要细细琢磨和努力,加油!