1. 无重复字符的最长字串
马上想到的就是双指针 + 滑动窗口,但是第一次回顾的时候没考虑到用哈希表。
注意 判断应该在while循环的开始,然后才是滑动窗口和判断结果。
if的判断条件除了可以用Map.get >= left 来判断
这里的if条件中加上 map.get(s[right]) >= left 是因为如果出现如abba 这种情况,在快指针指向第二个a的时候 满指针又会跳回第一个a之后的位置 也就是指向了第一个b,这是不对的。因为第一个a虽然是在哈希表里面,但是已经没有被滑动窗口包括了 因此我们不应该再往回指。
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function (s) {
let result = 0
let map = new Map()
let left = 0
let right = 0
while (right < s.length) {
//这里加map.get(s[right]>=left)的限制条件是因为如果出现abba 或者abcba这样的情况
//在不加限制条件的时候 左指针会跳回之前重复的字符,先出现的字符 但较晚才重复 就会出现这样的情况。
//加入这个限制条件 代表滑动窗口已经滑过去了 不需要再回到前面重复的字符的下标+1
if (map.has(s[right]) && map.get(s[right]) >= left) {
left = map.get(s[right]) + 1
}
map.set(s[right], right)
result = Math.max(right - left + 1, result)
right++
}
return result
};
2. 反转字符串I:
反转字符串和反转链表,首先第一反应应该想到的解法就是双指针。
因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。(原文)
/**
* @param {character[]} s
* @return {void} Do not return anything, modify s in-place instead.
*/
var reverseString = function(s) {
return s.reverse();
};
var reverseString = function(s) {
let l = -1, r = s.length;
while(++l < --r) [s[l], s[r]] = [s[r], s[l]];
return s;
};
(原文js代码)
function reverseString(str){
let left = 0
let right = str.length - 1
let middle = Math.floor((left + right) / 2)
for (let i = left, j = right; i < middle; i++){
[str[i], str[j]] = [str[j], str[i]]
}
return str
}
(笔者代码)
注意!JavaScript中 String没有reverse方法,本题是传一个字符数组进入函数中。
3. 反转字符串II
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例:
输入: s = "abcdefg", k = 2
输出: "bacdfeg"
首先把字符串分解为数组,用数组的方法进行反转更方便。左指针初始指向索引0,右指针指向索引0 + k - 1,判断一下边界情况,当索引 + k - 1超过数组长度 - 1的时候, 把右指针设置为数组长度-1的索引。while循环此时可以不用left <= right,因为当left= right的时候交换数组元素没有意义。
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("");
};
(原文js代码)
var reverseStr = function(str, k) {
let arr = Array.from(str)
for(let i = 0; i < arr.length; i += 2 * k){
reverse(arr, i, (i + k < arr.length ? (i + k - 1) : arr.length - 1))
}
return arr.join('')
};
function reverse(arr, left, right){
while(left < right){
let temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
left++
right--
}
}
(笔者代码) 实现思路基本与原文相同 可以优化的地方在于 reverse工具函数可以简单地使用一个while循环实现,原文中的代码十分简洁,巧妙地使用了++l,--r。
题目:剑指Offer 05.替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1: 输入:s = "We are happy."
输出:"We%20are%20happy."
笔者使用 replaceAll和split+join的库函数方法,但是时间复杂度可能比较高,因为没阅读过这三个库函数的源码,所以不清楚具体的时间复杂度。面试里面试官应该也不希望你直接调用库函数解决,不过话说回来,能够熟练地调用库函数也是一种能力。
/**
* @param {string} s
* @return {string}
*/
var replaceSpace = function(s) {
// 字符串转为数组
const strArr = Array.from(s);
let count = 0;
// 计算空格数量
for(let i = 0; i < strArr.length; i++) {
if (strArr[i] === ' ') {
count++;
}
}
let left = strArr.length - 1;
let right = strArr.length + count * 2 - 1;
while(left >= 0) {
if (strArr[left] === ' ') {
strArr[right--] = '0';
strArr[right--] = '2';
strArr[right--] = '%';
left--;
} else {
strArr[right--] = strArr[left--];
}
}
// 数组转字符串
return strArr.join('');
};
(原文js代码)
时间复杂度O(n)
空间复杂度O(1)
注意 是strArr[right--] = strArr[left--]不要写反了
242.有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = "anagram", t = "nagaram" 输出: true
示例 2: 输入: s = "rat", t = "car" 输出: false
说明: 你可以假设字符串只包含小写字母。
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
if(s.length !== t.length) return false;
const resSet = new Array(26).fill(0);
const base = "a".charCodeAt();
for(const i of s) {
resSet[i.charCodeAt() - base]++;
}
for(const i of t) {
if(!resSet[i.charCodeAt() - base]) return false;
resSet[i.charCodeAt() - base]--;
}
return true;
};
原文代码 使用哈希法映射。用resSet存储二十六个字母的映射,遍历第一个字符串,记录字母的数量。如果第二个字符串的字母数量和第一个不匹配就返回false。
时间复杂度为 O(n)
function isAnagram(s: string, t: string): boolean {
let strArr1: string[] = Array.from(s)
let strArr2: string[] = Array.from(t)
strArr1.sort()
strArr2.sort()
if(strArr2.join('') == strArr1.join(''))
return true
else
return false
};
笔者TypeScript代码
直接比较两个数组永远都是false因为JS比较引用类型的数据是比较内存地址,通过join方法让数组变成字符串之后再比较。