LeetCode探索字节跳动 :挑战字符串 (JavaScript)

386 阅读2分钟

无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

  示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列, 不是子串。

示例 4:

输入: s = ""
输出: 0

 

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

解题

维护数组:

遍历字符串,判断字符是否在滑动窗口数组里

  • 不在则 push 进数组
  • 在则删除滑动窗口数组里相同字符及相同字符前的字符,然后将当前字符 push 进数组
  • 然后将 max 更新为当前最长子串的长度
var lengthOfLongestSubstring = function(s) {
    let arr = [];
    let max_len = 0;
    
    s = s.split('');
    
    for(let i=0; i<s.length; i++){
        let index = arr.indexOf(s[i]);
        if(index !== -1){
            arr.splice(0,index+1);
        }
        arr.push(s[i]);
        // 比较获得最长substring
        max_len = Math.max(arr.length,max_len);
    }
    return max_len
};

最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

 

示例 1:

输入: strs = ["flower","flow","flight"]
输出: "fl"

示例 2:

输入: strs = ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

 

提示:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 仅由小写英文字母组成

解题

又 见 暴力破解

var longestCommonPrefix = function(strs) {
    let compare = strs[0];
    for(let i=1; i<strs.length; i++){
        if(strs[i].length < compare.length){
            compare = strs[i];
        }
    }
    let result = "";
    for(let i=0; i<compare.length; i++){
        let j=0
        for(; j<compare.length && j<strs[j].length; j++){
            if(compare.charAt(j) !== strs[j].charAt(j)) break
        }
        result = compare.substring(0,j);
    }
    if(result === '') return "";
    else return result;
};

字符串的排列

给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 ****的排列。如果是,返回 true ;否则,返回 false 。

换句话说,s1 的排列之一是 s2 的 子串 。

 

示例 1:

输入: s1 = "ab" s2 = "eidbaooo"
输出: true
解释: s2 包含 s1 的排列之一 ("ba").

示例 2:

输入: s1= "ab" s2 = "eidboaoo"
输出: false

 

提示:

  • 1 <= s1.length, s2.length <= 104
  • s1 和 s2 仅包含小写字母

解题

参考剑指offer38: 字符串的排序

先用dfs得到 s1 所有的排序,随后用String.prototype.includes()搜索s2

/**
 * @param {string} s1
 * @param {string} s2
 * @return {boolean}
 */
var checkInclusion = function(s1, s2) {
    function permutation(s){
        let arr = s1.split('');
        let len = s1.length;
        
        let flag = []; // 记录是否被遍历过
        let str = []; // 每次遍历的结果
        let result = []; // 最终所有遍历的结果
        
        function dfs(node){
            // 已遍历至最终节点
            if(node === len){
                // 将结果推入数组储存
                result.push(str.join(''));
                // 结束循环
                return
                
            }
            for(let i=0; i<len; i++){
                if(!flag[i]){
                    str[node] = arr[i];
                    flag[i] = 1;
                    // 进入下一层
                    dfs(node + 1);
                    flag[i] = 0;
                }
            }
        }
        // 从0开始遍历
        dfs(0);
        return result;
    }
    let list = permutation(s1);
    for(let i=0; i<list.length; i++){
        if(s2.includes(list[i])){
            return true
        }
    }
    return false;

};

字符串相乘

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"

说明:

  1. num1 和 num2 的长度小于110。
  2. num1 和 num2 只包含数字 0-9
  3. num1 和 num2 均不以零开头,除非是数字 0 本身。
  4. 不能使用任何标准库的大数类型(比如 BigInteger)直接将输入转换为整数来处理

解题

e.g. 11 = 1*1 + 10*1 + 10+1 + 10*10

var multiply = function(num1, num2) {
    let n1 = num1.split('');
    let n2 = num2.split('');
    let result = [];
    for(let i=0; i<n1.length; i++){
        for(let j=0; j<n2.length; j++){
            let each = (n1[i]*Math.pow(10,i))*(n2[j]*Math.pow(10,j));
            result.push(each);
        }  
    }
    return (eval(result.join('+')));
};

翻转字符串里的单词

给你一个字符串 s ,逐个翻转字符串中的所有 单词 。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。

说明:

  • 输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
  • 翻转后单词间应当仅用一个空格分隔。
  • 翻转后的字符串中不应包含额外的空格。

 

示例 1:

输入: s = "the sky is blue"
输出: "blue is sky the"

示例 2:

输入: s = "  hello world  "
输出: "world hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是翻转后的字符不能包括。

示例 3:

输入: s = "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将翻转后单词间的空格减少到只含一个。

示例 4:

输入: s = "  Bob    Loves  Alice   "
输出: "Alice Loves Bob"

示例 5:

输入: s = "Alice does not even like bob"
输出: "bob like even not does Alice"

 

提示:

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ' '
  • s 中 至少存在一个 单词

进阶:

  • 请尝试使用 O(1) 额外空间复杂度的原地解法。

解题

/**
 * @param {string} s
 * @return {string}
 */
var reverseWords = function(s) {
    return s.split(' ').filter(words => words !== '').reverse().join(' ') 
};

简化路径

给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。

请注意,返回的 规范路径 必须遵循下述格式:

  • 始终以斜杠 '/' 开头。
  • 两个目录名之间必须只有一个斜杠 '/' 。
  • 最后一个目录名(如果存在)不能 以 '/' 结尾。
  • 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.' 或 '..')。

返回简化后得到的 规范路径 。

  示例 1:

输入: path = "/home/"
输出: "/home"
解释: 注意,最后一个目录名后面没有斜杠。 

示例 2:

输入: path = "/../"
输出: "/"
解释: 从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。

示例 3:

输入: path = "/home//foo/"
输出: "/home/foo"
解释: 在规范路径中,多个连续斜杠需要用一个斜杠替换。

示例 4:

输入: path = "/a/./b/../../c/"
输出: "/c"

 

提示:

  • 1 <= path.length <= 3000
  • path 由英文字母,数字,'.''/' 或 '_' 组成。
  • path 是一个有效的 Unix 风格绝对路径。

解题

/**
 * @param {string} path
 * @return {string}
 */
var simplifyPath = function(path) {
    path = path.split('/');
    let ans = [];
    for(let i=0; i<path.length; i++){
        if(path[i] ==='..') {
            ans.pop();
        }
        else if(path[i]===''||path[i]==='.')
            continue;
        else{
            ans.push(path[i]);
        }
    }
    if(ans.length === 0){
        return '/';
    }
    else{
        let result = '/' + ans.join('/');
        return result;
    }
};

复原 IP 地址

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你不能重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

 

示例 1:

输入: s = "25525511135"
输出: ["255.255.11.135","255.255.111.35"]

示例 2:

输入: s = "0000"
输出: ["0.0.0.0"]

示例 3:

输入: s = "1111"
输出: ["1.1.1.1"]

示例 4:

输入: s = "010010"
输出: ["0.10.0.10","0.100.1.0"]

示例 5:

输入: s = "101023"
输出: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

 

提示:

  • 0 <= s.length <= 20
  • s 仅由数字组成

解题

/**
 * @param {string} s
 * @return {string[]}
 */
var restoreIpAddresses = function(str) {

  // 当前这里可以加一个判断 字符串的条件,字符串长度必须大于等于 4
  // 保存所有符合条件的 IP 地址
  let s = []
  let search = (cur, sub) => {
    // 递归的边界条件,数组长度等于 4 且组合起来与之前的字符串相等
    if (cur.length === 4 && cur.join('') === str) {
      s.push(cur.join('.'))
    } else {
      // 处理正常过程
      // 每次最多循环三次
      let len = Math.min(3, sub.length)
      for (let i = 0; i < len; i++) {
        // 截取字符串 1 ~ 3
        let tmp = sub.substr(0, i + 1)
        // 如果当前的数小于 256 说明在 255 范围内,接着递归调用(把不是范围内的排出掉)
        // 例如 255255255, 截取第一次 2,第二次递归截取时 for循环第三次是 522 不在范围内
        if(tmp < 256) {
          search(cur.concat([tmp]), sub.substr(i + 1))
        }
      }
    }
  }
  search([], str)
  return s

};