字符串-Leetcode

28.实现strStr()

28. 实现 strStr() - 力扣(LeetCode) (leetcode-cn.com)

字符串匹配是面试常见的问题

暴力解法O(m*n)

注意字符串的截取方法的差别

substr(start,number)

slice(start,end)

substring(start,end)

都接受两个参数

对于substr()来说,第一个参数表示开始的位置,第二个参数表示返回的子字符串串数量。

对于slice()和substrig(),第一个表示开始的位置,第二个表示截取到的位置

当参数为负数时,三个方法的行为不同:

slice()将所有负值参数当做字符串长度加上负参数值。

substr()把第一个参数当做字符串值加上该值,第二个负参数值转为0

substring()会将所有负参数值转为0,同时该方法会将较小的参数作为起点。

// 暴力
// 时间复杂度:O(m*n)
// 空间复杂度:O(1)
var strStr = function(haystack, needle) {
    // 暴力解法
    let m = haystack.length
    let n = needle.length
    // 如果是空字符串
    if(n==0) return 0
    if(m < n) return -1
    // 遍历求解
    for(let i = 0; i < m; i++) {
        // 找到和needle第一个字符相等的位置,然后截取判断
        if(haystack.charCodeAt(i) === needle.charCodeAt(0) && haystack.substr(i, n) === needle) {
            return i
        }
    }
    return -1
};

// KMP
// 时间复杂度:O(m + n)
// 空间复杂度:O(n)
var strStr = function(haystack, needle) {
    const m = haystack.length, n = needle.length
    if (n == 0) return 0
    if (m < n) return -1
    const nexts = getNexts(needle)

    let j = 0
    for (let i = 0; i < m; i++) {
        while (j > 0 && haystack[i] != needle[j]) {
            j = nexts[j - 1] + 1
        }
        if (haystack[i] == needle[j]) j++
        if (j == n) return i - n + 1
    }
    return -1
};

var getNexts = function(needle) {
    const n = needle.length
    if (n == 1) return []
    const nexts = new Array(n - 1).fill(0)
    nexts[0] = -1
    for (let j = 1; j < n - 1; j++) {
        let pre = nexts[j - 1]
        while (pre != -1 && needle[pre + 1] != needle[j]) {
            pre = nexts[pre]
        }
        if (needle[pre + 1] == needle[j]) pre++
        nexts[j] = pre
    }
    return nexts
}

459. 重复的子字符串

459. 重复的子字符串 - 力扣(LeetCode) (leetcode-cn.com)

方案1:双指针

假设子串的长度为1,比较相邻的两个,依次比较,直到遇到不相等,说明子串长度为1不能满足条件,子串的长度+1,假设子串的长度为2,再次进行比较。

image-20210728142238304

image-20210728144644180

/**
 * @param {string} s
 * @return {boolean}
 */
var repeatedSubstringPattern = function(s) {
   // 重复的子字符串的长度递增
    let n = s.length
    // 尝试的len
    for(let len = 1; len * 2 <= n; len++) {
        if( n % len === 0) {
            let matched = true
            // i指针从0开始
            let i = 0
            // j 从len开始
            for(let j = len; j < n; j++,i++) {
                if(s.charAt(i) !== s.charAt(j)) {
                    // 不匹配
                    matched = false
                    break
                }
            }
            // 没有不匹配的情况
            if(matched) return true
        
        }
    }
    return false
};

方案二:旋转数组

旋转数组之后的结果和原来的相同,说明是由重复的子字符串构成。

方案三:旋转优化

由上面的可知[a,c,d]旋转之后的结果可以是[d,a,c][c,d,a][a,c,d]

由重复字符串组成的字符串,旋转重复子字符串长度后得到本身

var repeatedSubstringPattern = function(s) {
    return s.repeat(2).slice(1, -1).includes(s);
};

344. 反转字符串

var reverseString = function(s) {
    let left = 0; right = s.length -1;
    while(left < right) {
        let temp = s[left]
        s[left] = s[right]
        s[right] = temp;
        left++;
        right--;
    }
};

扩展:反转字符串的某一部分

345. 反转字符串中的元音字母

/**
 * @param {string} s
 * @return {string}
 */
var reverseVowels = function(s) {
    let left = 0;
    let right = s.length - 1;
    let arr = s.split('')
    while(left < right) {
        while (left < right && !isVowel(arr[left])) left++
        while (left < right && !isVowel(arr[right])) right--

        [arr[left], arr[right]] = [arr[right], arr[left]]

        left++
        right--
    }
    return arr.join('')
};
// 是否是元音字母
var isVowel = function(c) {
    return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'
            || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U'
}

1119. 删除字符串中的元音

var removeVowels = function(s) {
    return s.split("").filter(c => !isVowel(c)).join("")
};

var isVowel = function(c) {
    return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'
}

541. 反转字符串2

541. 反转字符串 II - 力扣(LeetCode) (leetcode-cn.com)

var reverseStr = function(s, k) {
    const arr = s.split("")

    for (let start = 0; start < arr.length; start += 2 * k) {
        let left = start;
        let right = Math.min(left + k - 1, arr.length - 1);
        while (left < right) {
            [arr[left], arr[right]] = [arr[right], arr[left]]
            left++
            right--
        }
    }

    return arr.join("")
};

557. 反转字符串中的单词3

557. 反转字符串中的单词 III - 力扣(LeetCode) (leetcode-cn.com)

var reverseWords = function(s) {
    const n = s.length
    const arr = s.split("")
    let left = 0
    while (left < n) {
        if (arr[left] != ' ') {
            let right = left
            while (right + 1 < n && arr[right + 1] != ' ') right++
            reverseWord(arr, left, right)
            left = right + 1
        } else {
            left++
        }
    }

    return arr.join("")
};

var reverseWord = function(arr, left, right) {
    while (left < right) {
        [arr[left], arr[right]] = [arr[right], arr[left]]
        left++
        right--
    }
}

// 函数式编程
var reverseWords2 = function(s) {
    return s.split(" ").map(word => word.split("").reverse().join("")).join(" ")
}

58. 最后一个单词的长度

58. 最后一个单词的长度 - 力扣(LeetCode) (leetcode-cn.com)

解法1: 从左到右循环,找到最后一个单词。记录最后一个单词的长度

解法2: 从右到左

var lengthOfLastWord = function(s) {
    let len = s.length
    let end = len -1
    // 找到最后一个单词的结尾
    while(end >= 0 && s[end] === ' ') end--;
    if(end < 0) return 0;
    let start = end;
    while(start >= 0 && s[start] !== ' ') start--;
    return end - start

};

8. 字符串转为整数

8. 字符串转换整数 (atoi) - 力扣(LeetCode) (leetcode-cn.com)

var myAtoi = function(s) {
    // 1. 去掉空格
    let str = s.trim();
    if (parseInt(str)) {
        // 做边界处理
        return handler(parseInt(str));
    } else {
        return 0;
    }
};
const handler = function (num){
    if( num >= -Math.pow(2,31) && num <= Math.pow(2,31) -1 ) return num;
    else return num < 0 ? -Math.pow(2,31) : Math.pow(2,31) -1 ;
}

165. 比较版本号

165. 比较版本号 - 力扣(LeetCode) (leetcode-cn.com)

只要有一个没有处理完,就应该继续处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eytR0npM-1627957156092)(Day04字符串.assets/image-20210802205215703.png)]

/**
 * @param {string} version1
 * @param {string} version2
 * @return {number}
 */
var compareVersion = function(version1, version2) {
    let i1 = 0, i2 = 0
    let n1 = version1.length, n2 = version2.length
    while (i1 < n1 || i2 < n2) {
        let v1 = 0, v2 = 0
        while (i1 < n1 && version1[i1] != '.'){
            v1 = v1 * 10 + (version1[i1] - '0')
            i1++
        }
        while (i2 < n2 && version2[i2] != '.') {
            v2 = v2 * 10 + (version2[i2] - '0')
            i2++
        }
        if (v1 != v2) {
            return v1 > v2 ? 1 : -1
        }
        i1++
        i2++
    }
    return 0
};

12. 整数转罗马数字

12. 整数转罗马数字 - 力扣(LeetCode) (leetcode-cn.com)

799 = 500 + 100 + 100 + 90 + 9

找拆解后最小的

比799小的是500,减掉500后是299

比299小的是100,减掉之后是199

同上

尽量选大的,有贪心的思想

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPmvEj7R-1627957156095)(Day04字符串.assets/image-20210802213448672.png)]

var intToRoman = function(num) {
    const nums = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
    const romans = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
    let res = ""
    for (let index = 0; index < 13; index++) {
        while (num >= nums[index]) {
            res += romans[index]
            num -= nums[index]
        }
    }
    return res
};

13. 罗马数字转整数

13. 罗马数字转整数 - 力扣(LeetCode) (leetcode-cn.com)

通常情况下,罗马数字中小的数字再大的数字的右边。

所以我们定义两个指针,prev和curr,如果出现了prev小于curr的情况。则需要curr-prev。

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    let sum = 0;
    let prev = getValue(s[0])
    for(let i = 1; i <s.length;i++){
        const num = getValue(s[i])
        if(prev < num) {
            sum -= prev
        } else {
            sum += prev
        }
        prev = num
    }
    sum += prev
    return sum
};
var getValue = function(ch) {
    switch(ch) {
        case 'I': return 1
        case 'V': return 5
        case 'X': return 10
        case 'L': return 50
        case 'C': return 100
        case 'D': return 500
        case 'M': return 1000
        default: return 0;
    }
}

38. 外观数列

38. 外观数列 - 力扣(LeetCode) (leetcode-cn.com)

1
11
21
1211
111221
312211
var countAndSay = function(n) {
    // 第一行
    let curr = "1"
    for (let i = 1; i < n; i++) {
        const prev = curr
        curr = ""
        // 上一行的第一个字符,出现的数量为1
        let say = prev[0], count = 1
        for (let j = 1; j < prev.length; j++) {
            // 遍历上一行,出现和第一个重复的字符,数量+1
            if (prev[j] == say) {
                count++
            } else {
                // 出现不同字符的时候,把用于描述的数量和字符填进字符串
                curr += count
                curr += say
                // 重新计算下一个出现的字符和数量
                say = prev[j]
                count = 1
            }
        }
        // 加入最后一个字符及其数量
        curr += count
        curr += say
    }
    return curr
};

6. Z字形变换

6. Z 字形变换 - 力扣(LeetCode) (leetcode-cn.com)

image-20210803094627009

image-20210803100310098

image-20210803100756366

var convert = function(s, numRows) {
    if (numRows == 1) return s
    const n = s.length
    // 字符串数组
    const ret = new Array(Math.min(n, numRows)).fill("")
    // goingDown代表是否要转换方向
    let currRow = 0, goingDown = false;
    for (let i = 0; i < n; i++) {
        ret[currRow] += s[i]
        // 转换方向
        if (currRow == 0 || currRow == numRows - 1) goingDown = !goingDown
        if (goingDown) currRow++
        else currRow--
    }
    // 字符串拼接
    for (let i = 1; i < ret.length; i++) {
        ret[0] += ret[i]
    }

    return ret[0]
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值