day5 字符串应用

211 阅读7分钟

反转字符串

const str = 'juejin' 

const res = str.split('').reverse().join('') 
console.log(res) // nijeuj

回文字符串

正着读和倒着读都一样的字符串
例如:yessey

判断一个字符串是否是回文字符串

function isPalindrome(str) { 
    // 先反转字符串 
    const reversedStr = str.split('').reverse().join('')    
    // 判断反转前后是否相等 
    return reversedStr === str 
}

不难发现:回文字符串还有另一个特性:如果从中间位置“劈开”,那么两边的两个子串在内容上是完全对称

function isPalindrome(str) { 
    // 缓存字符串的长度 
    const len = str.length 
    // 遍历前半部分,判断和后半部分是否对称 
    for(let i=0;i<len/2;i++) { 
        if(str[i]!==str[len-i-1]) { 
            return false 
        } 
     } 
     return true 
}

真题

真题描述:给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串

示例 1: 输入: "aba"
输出: True
示例 2:
输入: "abca"
输出: True
解释: 你可以删除c字符。
注意: 字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。

☆ 关键词:对称性 和 双指针

1662086983437.png

image.png

当两个指针所指字符不同时候,不对称发生,那么就可以试试删掉一个字符的操作:看看区间在 [left+1, right] 或 [left, right-1] 的字符串是否回文

image.png

比如说这里我们跳过了 b,[left+1, right] 的区间就是 [2, 2],它对应 c 这个字符,单个字符一定回文。
左右指针内部区间是回文,外部区间也是回文,那么这就是回文字符串了✿✿ヽ(°▽°)ノ✿

const validPalindrome = function (s) {
    // 缓存字符串的长度
    const len = s.length

    // i、j分别为左右指针
    let i = 0,
        j = len - 1

    // 当左右指针均满足对称时,一起向中间前进
    while (i < j && s[i] === s[j]) {
        i++
        j--
    }

    // 尝试判断跳过左指针元素后字符串是否回文
    if (isPalindrome(i + 1, j)) {
        return true
    }
    // 尝试判断跳过右指针元素后字符串是否回文
    if (isPalindrome(i, j - 1)) {
        return true
    }

    // 工具方法,用于判断字符串是否回文
    function isPalindrome(st, ed) {
        while (st < ed) {
            if (s[st] !== s[ed]) {
                return false
            }
            st++
            ed--
        }
        return true
    }

    // 默认返回 false
    return false
};

正则表达式

真题描述: 设计一个支持以下两种操作的数据结构:
void addWord(word)
bool search(word)
search(word) 可以搜索文字或正则表达式字符串,字符串只包含字母 . 或 a-z 。
. 可以表示任何一个字母。

示例: addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true
说明:
你可以假设所有单词都是由小写字母 a-z 组成的。

思路分析:
添加方法:利用对象存储键值对
② 降低查找复杂度,以字符串长度为key,即相同长度的字符串放在同一个数组

/**
 * 构造函数
 */
const WordDictionary = function () {
    // 初始化一个对象字面量,承担 Map 的角色
    this.words = {}
};

/**
  添加字符串的方法
 */
WordDictionary.prototype.addWord = function (word) {
    // 若该字符串对应长度的数组已经存在,则只做添加
    if (this.words[word.length]) {
        this.words[word.length].push(word)
    } else {
        // 若该字符串对应长度的数组还不存在,则先创建
        this.words[word.length] = [word]
    }

};

搜索方法
① 判断输入内容是纯字符串还是正则表达式
② 字符串的话,直接到对应key组找
③ 正则表达式的话,创建正则表达式对象,判断 Map 中相同长度的字符串里,是否存在一个能够与这个正则相匹配

/**
  搜索方法
 */
WordDictionary.prototype.search = function (word) {
    // 若该字符串长度在 Map 中对应的数组根本不存在,则可判断该字符串不存在
    if (!this.words[word.length]) {
        return false
    }
    // 缓存目标字符串的长度
    const len = word.length
    // 如果字符串中不包含‘.’,那么一定是普通字符串
    if (!word.includes('.')) {
        // 定位到和目标字符串长度一致的字符串数组,在其中查找是否存在该字符串
        return this.words[len].includes(word)

    }

    // 否则是正则表达式,要先创建正则表达式对象
    const reg = new RegExp(word)

    // 只要数组中有一个匹配正则表达式的字符串,就返回true
    return this.words[len].some((item) => {
        return reg.test(item)
    })
};

字符串与数字之间的转换问题

题目:请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明: 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31,  2^31 − 1]。如果数值超过这个范围,请返回  INT_MAX (2^31 − 1) 或 INT_MIN (−2^31) 。\

示例 1:
输入: "42"
输出: 42\

示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3: 输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。

示例 4: 输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 因此无法执行有效的转换。

示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。因此返回 INT_MIN (−2^31) 。

解题思路:

1、 去空格一直找到非空格字符;
2、 识别开头的“+”字符和“-”字符,它能和后面数字组合起来;
3、 见到非整数字符后可以刹车了;
4、 题目明确的数值范围:[−2^31, 2^31 − 1]

// 计算最大值 
const max = Math.pow(2,31) - 1 
// 计算最小值 
const min = -max - 1

// 去空格
let str = '        +10086' 
str.trim() // '+10086'
// 用正则表达式匹配
/\s*([-\+]?[0-9]*).*/

\s*,这里的意思就是空格出现0次或多次,都可被匹配到。
() 圈住的内容,就是我们要捕获起来额外存储的东西
[]中的匹配符之间是“或”的关系
[0-9]*0-9之间的整数,能匹配到0个或多个就算匹配成功
.这个是任意字符的意思
.*用于字符串尾部匹配非数字的任意字符\

const reg = /\s*([-\+]?[0-9]*).*/ 
const groups = str.match(reg)

/*
1、 match() 方法是一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null;
2、正则表达式尾部有 g 标志,match()会返回与完整正则表达式匹配的所有结果,但不会返回捕获组;
3、上面正则表达式分析中,只定义了一个捕获组,因从 groups[1] 里拿到我们捕获的结果
*/

完整代码↓

// 入参是一个字符串
const myAtoi = function (str) {
    // 编写正则表达式
    const reg = /\s*([-\+]?[0-9]*).*/
    // 得到捕获组
    const groups = str.match(reg)
    // 计算最大值
    const max = Math.pow(2, 31) - 1
    // 计算最小值
    const min = -max - 1
    // targetNum 用于存储转化出来的数字
    let targetNum = 0
    // 如果匹配成功
    if (groups) {
        // 尝试转化捕获到的结构
        targetNum = +groups[1]
        // 注意,即便成功,也可能出现非数字的情况,比如单一个'+'
        if (isNaN(targetNum)) {
            // 不能进行有效的转换时,请返回 0
            targetNum = 0
        }
    }
    // 卡口判断
    if (targetNum > max) {
        return max
    } else if (targetNum < min) {
        return min
    }
    // 返回转换结果
    return targetNum
};