阅读 153

字符串专题

1.驼峰转换(正则)

驼峰命名camelCase和短横线命名kebab-case

(1)将'getElementById'转换成'get-element-by-id'

const getKebabCase = function(str) {   // str.replace不会改变原字符串,因此需要直接return   return str.replace(/[A-Z]/g, (i) => '-' + i.toLowerCase()); } 复制代码

(2)将'get-element-by-id'转换成'getElementById'

const getCamelCase = function(str) {   // /w匹配任意的字母、数字和下划线   // $0,$1匹配成功的第n组内容   return str.replace(/-(\w)/g, ($0, $1) => $1.toUpperCase()); } 复制代码

2.无重复字符的最长子串(3)

滑动窗口 O(n):

使用两个指针lt,rt表示滑动窗口的左右边界,使用map结构来存放滑动窗口中的字符串,key代表该字符,value代表该字符的索引。

每当向右移动右指针时,都会先判断map结构是否包含该字符,是就更新左指针,不论是否包含都需要更新map、更新res。

const longestSubString = function(str) {   let lt = rt = res = 0;   const map = new Map();     for(; rt < str.length; rt++) {     if (map.has(str[rt])) {  // 滑动窗口有重复字符,右移lt       lt = Math.max(lt, map.get(str[rt]) + 1);  // 必须用max,为了保持lt不倒退     }     map.set(str[rt], rt);  // key是字符,value是字符的索引,方便lt的变化     res = Math.max(res, rt - lt + 1);   }   return res; } 复制代码

3.比较版本号(165)

spilt + parseInt O(m + n):

const compareVersion = function(version1, version2) {   const v1 = version1.split('.'),         v2 = version2.split('.');   // 遍历的长度取两个版本号的最长长度   for (let i = 0; i < v1.length || i < v2.length; i++) {     let num1 = i < v1.length ? parseInt(v1[i]) : 0,         num2 = i < v2.length ? parseInt(v2[i]) : 0;     if (num1 > num2)  return 1;     if (num1 < num2)  return -1;   }   return 0; } 复制代码

4.字符串相加(415)

常规思路O(max(m, n)):模拟竖式加法,定义两个指针p1和p2分别指向两个数字的末尾,定义变量add维护当前进位,另外,对于位数较短的数字进行了补0操作。

优化:对补0操作优化,比如一个长度很长和一个长度很短的字符做加法,可以只对后面几位做加法。

const addStrings = function(s1, s2) {   let p1 = s1.length - 1, p2 = s2.length - 1;    // p指针初始指向个位   let add = 0, res = [];   // 进位        while (p1 >= 0 || p2 >= 0 || add) {   //@ 进位add不为0     // 因为字符串不支持直接修改,只能取出字符进行赋值操作     let ch1 = s1[p1] ? s1[p1] : '0',         ch2 = s2[p2] ? s2[p2] : '0';     let sum = parseInt(ch1) + parseInt(ch2) + add;     add = parseInt(sum / 10);     res.push(sum % 10);     p1 -= 1;     p2 -= 1;   }   return res.reverse().join('');  //@ reverse } 复制代码

5.有效的括号(20)

栈O(n):

遍历字符串s,当遇到左括号时会入栈,遇到右括号时、如果此时栈为空或者字符不等于栈顶元素,那就肯定不是有效括号,如果等于栈顶符号就出栈。最后通过判断栈大小返回布尔值。

const isValid = function(s) {    if (s.length % 2 === 1) return false;  // 长度为奇数长,无效括号   const map = new Map([     [')', '('],     ['}', '{'],     [']', '['],   ]);   const stack = [];   for (let ch of s) {  // 比for循环更高效     if (map.has(ch)) {  // 右括号       // 如果stack为空,或者不等于栈顶元素       if (!stack.length || stack[stack.length - 1] !== map.get(ch)) {         return false;       }        stack.pop();     } else {  // 左括号       stack.push(ch);     }   }   return !stack.length;  //@ 同时包含两种情况,或者两个if代替 } 复制代码

6.最长回文子串(5)

中心扩散法 O(n^2):

空间复杂度O(1)

const longetSubString = function(s) {   if (s.length < 2) return s;   let res = '';   // 遍历字符串,提取每个字符的回文串   function sliceString(left, right) {     while (left >= 0 && right < s.length && s[left] === s[right]) {       left -= 1;  // 同步扩大范围       right += 1;     }     // 跳出循环,此时left和right并不满足,left + 1和right - 1才满足     let len = (right - 1) - (left + 1) + 1;     res = len > res.length ? s.slice(left + 1, right)  : res;  // slice提取不包含right字符   }   for (let i = 0; i < s.length; i++)  {     // 假设回文串是奇数个     sliceString(i, i);     // 假设回文串是偶数个     if (i + 1 < s.length) {       sliceString(i, i + 1);     }   }   return res; } 复制代码

7.最长公共前缀(14)

初始化最长公共前缀s的值是第一个字符串,遍历后面的字符串、依次进行比较,最终结果即为最长公共前缀。如果查找过程中出现s为空的情况,则直接返回""。

时间复杂度 O(s)。

const longestCommonPrefix = function(strs) {   if (strs.length === 1)  return "";   let s = strs[0];   for (let i = 1; i < strs.length; i++) {     let j = 0;     for (; j < Math.min(s.length, strs[i].length); j++) {       if (s[j] !== strs[i][j]) {  // 字符不相等         break;       }      }     // 如果放在第2层循环里面,由于j遍历的范围,当有一方长度为1时,会导致一次也进入不了if判断,直接返回s     s = s.substr(0, j);  //@ 位置放在第2层for循环中     if (!s) return s;     }   return s; } 复制代码

8.括号生成(22)

DFS:

首先,一个合法的括号序列需要满足两个条件:(1)左右括号数量相等;(2)任意前缀中左括号数量 >= 右括号数量,这样就能保证每个右括号都能找到相匹配的左括号。

设计思想:将生成n对合法括号序列组合的问题,转化为序列的每一位要填什么的问题。 因为合法的括号序列,任意前缀中左括号数量一定大于等于右括号数量,所以(1)如果左括号数量不大于等于n时,我们就可以放一个左括号 (2)如果右括号数量小于左括号的数量,我们就可以放一个右括号。

搜索过程:function dfs (n, lc, rc, str)。 1.初始时左括号数量lc和右括号数量rc都为0。 2. 如果 lc小于括号对数n, 那在当前合法序列str后拼接左括号。3.如果右括号数量小于左括号数量,则在当前序列str后拼接右括号。 4.当lc和rc数量都等于n时,将当前合法序列str加入结果数组res中。

时间复杂度:经典卡特兰数问题,O(Cn-2n),结合图理解。

const res = [];  // 全局变量 function dfs(n, lc, rc, str) {   if (lc == n && rc == n) {     res.push(str);   }   else {   //@ 注意结构,两个if都要走     if (lc < n) dfs(n, lc + 1, rc, str + "(");     if (rc < n && lc > rc)  dfs(n, lc, rc + 1, str +")");   } } const generateValidString = function(n) {   dfs(n, 0, 0, "");   return res; }


作者:二阶导
链接:https://juejin.cn/post/7014112315020673060


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐