“等下一个天亮,去上次牵手赏花那里散步好吗”
——《下一个天亮》
力扣题目链接(opens new window)
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
不借助库函数的情况下,我们就用两个指针实现头和尾的交换,然后向中间靠近,
var reverseString = function(s) {
let l = -1, r = s.length;
while(++l < --r) [s[l], s[r]] = [s[r], s[l]];
};
力扣题目链接(opens new window)
给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例:
输入: s = "abcdefg", k = 2
输出: "bacdfeg"
和上一题一样,我们采用相同的交换逻辑,只是加一点判断条件。
左右指针的位置和以前不一样了,我们要根据长度调整:我们写出一个for循环,然后条件是
(let i = 0; i < len; i += 2 * k )这时候i在2*k的基础上进行增加,我们让做左指针变成i-1,右指针进行判断,看剩余部分的情况。这种做法很聪明,第一遍循环的时候,我们的i刚好是0,到第二天循环的时候i已经变成了2*k了。这种循环帮我们解决了控制字符串分割的问题,我们只要做好交换逻辑就好。
代码如下:
var reverseStr = function(s, k) {
const len = s.length;
let resArr = s.split("");
for(let i = 0; i < len; i += 2 * k) {
let l = i - 1, r = i + k > len ? len : i + k;
while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]];
}
return resArr.join("");
};
卡码网题目链接(opens new window)
给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。
例如,对于输入字符串 "a1b2c3",函数应该将其转换为 "anumberbnumbercnumber"。
对于输入字符串 "a5b",函数应该将其转换为 "anumberb"
输入:一个字符串 s,s 仅包含小写字母和数字字符。
输出:打印一个新的字符串,其中每个数字字符都被替换为了number
样例输入:a1b2c3
样例输出:anumberbnumbercnumber
数据范围:1 <= s.length < 10000。
这道题就是数组填充,很简单,找到数字从这位开始往后面填充number。
代码如下:
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
function main() {
const num0 = "0".charCodeAt();
const num9 = "9".charCodeAt();
const a = "a".charCodeAt();
const z = "z".charCodeAt();
function isAZ(str) {
return str >= a && str <= z;
}
function isNumber(str) {
return str >= num0 && str <= num9;
}
rl.on("line", (input) => {
let n = 0;
for (let i = 0; i < input.length; i++) {
const val = input[i].charCodeAt();
if (isNumber(val)) {
n+= 6;
}
if (isAZ(val)) {
n++;
}
}
const ans = new Array(n).fill(0);
let index = input.length - 1;
for (let i = n - 1; i >= 0; i--) {
const val = input[index].charCodeAt();
if (isAZ(val)) {
ans[i] = input[index];
}
if (isNumber(val)) {
ans[i] = "r";
ans[i - 1] = "e";
ans[i - 2] = "b";
ans[i - 3] = "m";
ans[i - 4] = "u";
ans[i - 5] = "n";
i -= 5;
}
index--;
}
console.log(ans.join(""));
})
}
main();
力扣题目链接(opens new window)
给定一个字符串,逐个翻转字符串中的每个单词。
示例 1:
输入: "the sky is blue"
输出: "blue is sky the"示例 2:
输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。示例 3:
输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
这道题需要一点神奇配方,我们首先想到的就是把每个单词单独拿出来反转后,在将反转的单词的顺序反转。但是:我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。
那很好了!!!
下场操作!:
var reverseWords = function(s) {
// 字符串转数组
const strArr = Array.from(s);
// 移除多余空格
removeExtraSpaces(strArr);
// 翻转
reverse(strArr, 0, strArr.length - 1);
let start = 0;
for(let i = 0; i <= strArr.length; i++) {
if (strArr[i] === ' ' || i === strArr.length) {
// 翻转单词
reverse(strArr, start, i - 1);
start = i + 1;
}
}
return strArr.join('');
};
// 删除多余空格
function removeExtraSpaces(strArr) {
let slowIndex = 0;
let fastIndex = 0;
while(fastIndex < strArr.length) {
// 移除开始位置和重复的空格
if (strArr[fastIndex] === ' ' && (fastIndex === 0 || strArr[fastIndex - 1] === ' ')) {
fastIndex++;
} else {
strArr[slowIndex++] = strArr[fastIndex++];
}
}
// 移除末尾空格
strArr.length = strArr[slowIndex - 1] === ' ' ? slowIndex - 1 : slowIndex;
}
// 翻转从 start 到 end 的字符
function reverse(strArr, start, end) {
let left = start;
let right = end;
while(left < right) {
// 交换
[strArr[left], strArr[right]] = [strArr[right], strArr[left]];
left++;
right--;
}
}
卡码网题目链接(opens new window)
字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
例如,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。
输入:输入共包含两行,第一行为一个正整数 k,代表右旋转的位数。第二行为字符串 s,代表需要旋转的字符串。
输出:输出共一行,为进行了右旋转操作后的字符串。
样例输入:
2 abcdefg
1
2样例输出:
fgabcde
1
数据范围:1 <= k < 10000, 1 <= s.length < 10000;
这道题和上一道题有异曲同工之妙,都是要我们有逆向思维。在这里借用一下代码随想录的图:
思路就是 通过 整体倒叙,把两段子串顺序颠倒,两个段子串里的的字符在倒叙一把,负负得正,这样就不影响子串里面字符的顺序了。
我们先将整体反转,在将长度n和length-n的字串分别反转过来就能实现了。
代码如下:
const readline = require('readline')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
const inputs = []; // 存储输入
rl.on('line', function(data) {
inputs.push(data);
}).on('close', function() {
const res = deal(inputs);
// 打印结果
console.log(res);
})
// 对传入的数据进行处理
function deal(inputs) {
let [k, s] = inputs;
const len = s.length - 1;
k = parseInt(k);
str = s.split('');
str = reverseStr(str, 0, len - k)
str = reverseStr(str, len - k + 1, len)
str = reverseStr(str, 0, len)
return str.join('');
}
// 根据提供的范围进行翻转
function reverseStr(s, start, end) {
while (start < end) {
[s[start], s[end]] = [s[end], s[start]]
start++;
end--;
}
return s;
力扣题目链接(opens new window)
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1: 输入: haystack = "hello", needle = "ll" 输出: 2
示例 2: 输入: haystack = "aaaaa", needle = "bba" 输出: -1
说明: 当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。 对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
这道题是KMP模式匹配的题目,主包在上数据结构时学过这个算法,所以只要掌握了KMP算法就能解决这一类问题》
如果不了解KMP的话可以去代码随想录
代码奉上:
var strStr = function (haystack, needle) {
if (needle.length === 0)
return 0;
const getNext = (needle) => {
let next = [];
let j = -1;
next.push(j);
for (let i = 1; i < needle.length; ++i) {
while (j >= 0 && needle[i] !== needle[j + 1])
j = next[j];
if (needle[i] === needle[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;
};
力扣题目链接(opens new window)
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
- 输入: "abab"
- 输出: True
- 解释: 可由子字符串 "ab" 重复两次构成。
示例 2:
- 输入: "aba"
- 输出: False
示例 3:
- 输入: "abcabcabcabc"
- 输出: True
- 解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)
暴力:
我们暴力遍历每个字串,看他们重复之后能不能和原字符串一致(因为他说“检查是否可以通过由它的一个子串重复多次构成”),一致的话就说明可以有字串组成。
var repeatedSubstringPattern = function(s) {
const n = s.length;
// 遍历所有可能的子串长度
for (let k = 1; k <= n / 2; k++) {
// 如果 n 不能被 k 整除,跳过
if (n % k !== 0) {
continue;
}
// 提取子串
const substring = s.slice(0, k);
// 检查子串重复后是否与原字符串一致
let repeatedString = "";
for (let i = 0; i < n / k; i++) {
repeatedString += substring;
}
if (repeatedString === s) {
return true;
}
}
return false;
};
接下来介绍:移动匹配(借用代码随想录的图)
当一个字符串s:abcabc,内部由重复的子串组成,那么这个字符串的结构一定是这样的:
也就是由前后相同的子串组成。
那么既然前面有相同的子串,后面有相同的子串,用 s + s,这样组成的字符串中,后面的子串做前串,前面的子串做后串,就一定还能组成一个s,如图:
当然,我们在判断 s + s 拼接的字符串里是否出现一个s的的时候,要刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s,我们要搜索的是中间拼接出来的s。
如果有一个字符串s,在 s + s 拼接后, 不算首尾字符,如果能凑成s字符串,说明s 一定是重复子串组成。
代码实现:
var repeatedSubstringPattern = function(s) {
const doubledString = s + s;
// 去掉拼接字符串的首尾字符
const modifiedString = doubledString.slice(1, -1);
// 检查去掉首尾字符后的字符串中是否包含完整的 s
return modifiedString.includes(s);
};
短短三行,你说这玩意谁研究的呢,太强了!!!!