继续练习leetcode中关于字符串的算法题,越练越觉得自己编码思想还很欠缺,继续努力。
文章目录
- 有效的括号
- 括号生成
- 串联所有单词的子串
- 最长有效括号
有效的括号
leetcode链接:https://leetcode.cn/problems/valid-parentheses/
解题思路:无论这三种符号谁在里谁在外,或者单独存在,只要是有效的括号,我们都可以通过将字符串中()或者[]或者{}替换成"",下一次循环替换时,外层的括号也会被替换,直到最外层。
理论上最多需要替换字符串长度的二分之一,因为括号总是成对出现,如果一直到最后都没能将字符串彻底清空,表明有不成对的括号出现,即不是有效的括号。
/** https://leetcode.cn/problems/valid-parentheses/
* @param {string} s
* @return {boolean}
*/
var isValid = function (s) {
// 先判断是否是偶数个
if (typeof s !== "string" || s.length % 2) {
return false;
}
const length = s.length;
for (let i = 0; i <= length / 2; i++) {
s = s.replace("()", "").replace("[]", "").replace("{}", "");
if (s.length === 0) {
break;
}
}
return s.length === 0;
};
括号生成
leetcode 链接:https://leetcode.cn/problems/generate-parentheses/
解题思路:要生成n对()的字符串,就意味着要递归n次,每一次又要在之前的字符串的每一个位置尝试着添加,循环上一次结果的长度次数。
比如当n为3时,需要递归三次,第一次就是一个"()“,第二次就在第一次的基础上,分别从0的位置一直到最后的位置尝试加”()“,所以第二次可能是”()()“,”(())“,”()()",然后发现1和3相同,所以只保存一个,将第二次循环的每一个不重复的结果进行第三次递归。
所以,我们n的数,其实就是递归的次数,每一次递归里面都需要循环,循环的次数就是上一次递归结果的长度。
/** https://leetcode.cn/problems/generate-parentheses/
* @param {number} n
* @return {string[]}
*/
var generateParenthesis = function (n) {
const result = [];
if (typeof n !== "number" || n <= 0) {
return result;
}
let otherStr = [];
const generateString = function (n, s) {
for (let i = 0; i <= s.length; i++) {
// 在s的每一个位置分别插入()
let newStr = s.slice(0, i) + "()" + s.slice(i);
if (n === newStr.length / 2) {
// 最后要检查一下result中是否已经有了该字符串,可能出现重复的情况,比如将()放在 0和最后的位置,对于 "()"来说是一样的
if (!result.includes(newStr)) {
result.push(newStr);
}
} else {
if (!otherStr.includes(newStr)) {
// 如果otherStr中已经出现过,表明之前已经执行了,不需要再放入
otherStr.push(newStr);
generateString(n, newStr);
}
// generateString(n, newStr)
}
}
};
generateString(n, "");
return result;
};
串联所有单词的子串
leetcode 链接:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/
解题思路:先将s按照要求的长度(words里面所有元素加起来的长度)进行切割片段,切割出来的每一个片段进行循环判断,是否是由words里面的元素随机顺序组合出来的。
如何判断?将每一个片段按照words里单个元素的长度再次进行切割,将这些小小的片段去words里面一一匹配,只要全部都能匹配上,就符合要求,否则不符合。并把这些片段在s中的坐标返回即可。
/**
* @param {string} s https://leetcode.cn/problems/substring-with-concatenation-of-all-words/
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function (s, words) {
// 先判断一下words的元素的长度和words的长度
let length = 0;
let result = [];
if (words.length) {
length = words.length * words[0].length;
}
const array = [];
// 将s字符串按照length的长度进行切割,并将所有切割后的内容存放到数组中,必须得保存当前的下标,这个下标需要精准返回。
for (let i = 0; i <= s.length - length; i++) {
array.push({ string: s.slice(i, i + length), index: i });
}
// 我们需要遍历这个数组,并将里面每一个元素字符串按照words[0].length 的长度切割成一个个小的字符串
array.forEach((item) => {
let itemSplitArray = [];
let i = 0;
while (i < item.string.length) {
itemSplitArray.push(item.string.slice(i, i + words[0].length));
i = i + words[0].length;
}
// 切割出来的这个itemSplitArray的元素需要去words中进行匹配,以确保每一个元素都在words中能够找到,并且不是重复的。
let newWords = JSON.parse(JSON.stringify(words));
let isSuccess = true;
for (const split of itemSplitArray) {
let index = newWords.findIndex((findItem) => {
return split === findItem;
});
if (index >= 0) {
newWords[index] = undefined;
} else {
// 只要有一个没找到即index为-1,就认为这个item.string不符合条件,就找下一个。
isSuccess = false;
break;
}
}
if (isSuccess) {
result.push(item.index);
}
});
return result;
};
最长有效括号
leetcode 链接:https://leetcode.cn/problems/longest-valid-parentheses/
这道题我用了两种解法,第一种解法能行得通,容易理解,但是效率很低
甚至不能通过leetcode的效率检测。逻辑和目的实际上没问题。
/** 最长有效括号 https://leetcode.cn/problems/longest-valid-parentheses/
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function (s) {
// 最长有效,我们就从最长开始
let targetArray = [];
for (let i = s.length; i >= 2; i--) {
if (i % 2 !== 0) {
continue;
}
for (let j = 0; j <= s.length - i; j++) {
let targetStr = s.slice(j, j + i);
targetArray.push(targetStr);
}
}
// 我们不在双重for循环里判断targetStr是不是符合要求的有效括号,提出来可能判断可能会好一些,数组最前面的item越长,
// 所以按照数组的顺序,只要判断出当前的item符合要求就直接返回结果,因为剩下的即使符合要求,也比当前的item短
for (const item of targetArray) {
let newItem = item;
// 判断是否是有效括号其实很简单,通过之前的题目,我们知道,将字符串里面的()全部替换成“”,重复数次即可
for (let n = 0; n <= item.length / 2; n++) {
newItem = newItem.replace("()", "");
if (newItem.length === 0) {
return item.length;
} else if (newItem.length % 2 !== 0) {
break;
}
}
if (newItem.length === 0) {
return item.length;
}
}
// 如果整个targetArray遍历完了都找不到符合要求的,直接返回0
return 0;
};
第二种解法利用栈stack的思想,遇到"("
即入栈,遇到")"
即出栈,但对于最长有效长度的认定,的确需要认真思考。
如果遇到)出栈后还为空,就将当前的下标作为参考值(和最开始的-1一样,都是参考值),存入栈中。
/** 最长有效括号 https://leetcode.cn/problems/longest-valid-parentheses/
* @param {string} s
* @return {number}
*/
var longestValidParentheses = (s) => {
let maxLen = 0;
const stack = [];
stack.push(-1);
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c == '(') { // 左括号的索引,入栈
stack.push(i);
} else { // 遍历到右括号
stack.pop(); // 栈顶的左括号被匹配,出栈
if (stack.length) { // 栈未空
const curMaxLen = i - stack[stack.length - 1]; // 计算有效连续长度
maxLen = Math.max(maxLen, curMaxLen); // 挑战最大值
} else { // 栈空了
stack.push(i); // 入栈充当参照
}
}
}
return maxLen;
};