代码随想录阅读笔记二:字符串

140 阅读3分钟

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:

反转字符串和反转链表,首先第一反应应该想到的解法就是双指针。

因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。(原文)

008eGmZEly1gp0fvi91pfg30de0akwnq.gif

/**
 * @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.替换空格

力扣题目链接(opens new window)

请实现一个函数,把字符串 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.有效的字母异位词

力扣题目链接(opens new window)

给定两个字符串 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方法让数组变成字符串之后再比较。