最近看公众号推送被安利了一个vscode神器, leetcode 插件,可以直接在vscode里编写代码,然后直接提交leetcode 测试,加上可以直接用vscode debug,这样就可以全程不打开网页比较方便的刷题了,作为一个刷题新手,就从最简单的字符串类题目开始吧
题目
/*
* @lc app=leetcode.cn id=14 lang=javascript
*
* [14] 最长公共前缀
*
* https://leetcode-cn.com/problems/longest-common-prefix/description/
*
* algorithms
* Easy (31.50%)
* Total Accepted: 57.1K
* Total Submissions: 177K
* Testcase Example: '["flower","flow","flight"]'
*
* 编写一个函数来查找字符串数组中的最长公共前缀。
*
* 如果不存在公共前缀,返回空字符串 ""。
*
* 示例 1:
*
* 输入: ["flower","flow","flight"]
* 输出: "fl"
*
*
* 示例 2:
*
* 输入: ["dog","racecar","car"]
* 输出: ""
* 解释: 输入不存在公共前缀。
*
*
* 说明:
*
* 所有输入只包含小写字母 a-z 。
*
*/
/**
* @param {string[]} strs
* @return {string}
*/
第一想法
看到这个题目,我脑子里第一时间冒出来了 ES6 的 Set,当时的思路是,遍历整个数组.然后遍历所有字符串,将字符串的第n位添加到set里,如果这个set 的长度大于1,循环就可以终止,就可以求出结果.然后就开始撸起了代码,
第一版如下
// 遍历字符串, 然后push 到set 里 哪个set长度为2 即终止
var longestCommonPrefix = function(strs) {
let len = strs[0].length
let sets = []
let x;
for(let i=0; i<len; i++){
sets[i] = new Set()
strs.forEach(item => {
if (!item[i]){
return ""
}
sets[i].add(item[i])
})
if (sets[i].size > 1) {
return strs[0].slice(0, i)
}
}
};
本地跑第一个用例通过,兴冲冲的点击提交.结果是残酷的
- 执行出错信息: Line 43: TypeError: Cannot read property 'length' of undefined
- 最后执行的输入:[] 呀,没有考虑到数组为空的情况,这样把,加上一个判断
第二次提交
函数一开始加个判断
if(!strs[0]){
return ""
}
当然也是不通过啦,但是有进步啦,至少这次通过了73个测试用例了
- 73 / 118 个通过测试用例 状态:解答错误
- 输入:["a"]
- 输出:undefined
- 预期:"a"
又没考虑到边界情况, 得,再加上个判断吧
第三次提交
函数开始再加个判断
if(strs.length === 1){
return strs[0]
}
再来, emmm, 进步了一位
- 74 / 118 个通过测试用例 状态:解答错误
- 输入:["c","c"]
- 输出:undefined
- 预期:"c"
难受啊,咋这么多个奇葩的情况
第四次提交
在函数最后加上默认情况
return strs[0]
这次前进了30位啦,就差最好16个测试用例就成功啦
- 102 / 118 个通过测试用例 状态:解答错误
- 输入:["aa","a"]
- 输出:"aa"
- 预期:"a"
这个地方卡了好久,我之前有判断过如果后面的字符串长度有小于第一个字符串的时候就直接return, 但是为什么没生效呢,
if (!item[i]){
return ""
}
通过vscode debug 发现,原来return 出了 内层的forEach 之后,外层的for 循环居然照样执行,行吧,那在里面标记下.如果外层出现了这个标记,就也return出去(中间去搜了下js跳出多重循环,但是不知道为什么都是报错.不知道网上那些博客怎么写出来的.break,break tag之类的,发现我直接用就报错,不合法,所以才采用标记位这种方法)
第五次提交
let x = ''
for(var i=0; i<len; i++){
sets[i] = new Set()
strs.forEach(item => {
if (!item[i]) {
x = i
return strs[0].slice(0, i - 1)
}
sets[i].add(item[i])
})
// console.log(sets[i])
if (sets[i].size > 1) {
return strs[0].slice(0, i)
}
}
if(x) {
return strs[0].slice(0, i - 1)
}
return strs[0
当然也不期待直接就过了.但是有进步呀,
- 110 / 118 个通过测试用例 状态:解答错误
- 输入:["abab","aba",""]
- 输出:"aba"
- 预期:""
又是没有考虑到边界情况,行吧,再加一个标记判断一下边界情况
第六次提交
完整代码
var longestCommonPrefix = function(strs) {
if(!strs[0]){
return ""
}
if(strs.length === 1){
return strs[0]
}
let len = strs[0].length
let sets = []
let x = ''
for(var i=0; i<len; i++){
sets[i] = new Set()
strs.forEach(item => {
if(!item){
x= 'f'
return
}
if (!item[i]) {
x = i
return
}
sets[i].add(item[i])
})
if(x === 'f'){
return ""
}
if(x) {
return strs[0].slice(0, i)
}
if (sets[i].size > 1) {
return strs[0].slice(0, i)
}
}
return strs[0]
};
终于:
- 118 / 118 个通过测试用例状态:通过
- 执行用时:152 ms
- 击败了6.84% JavaScript的提交记录 感动的哭了,终于通过了.虽然整个解法比较ugly,自己都有点看不过去.但是也是自己的一种思路,一步步的趟过来,也算实现了.
一共花了40分钟,真不容易啊,这么简单的一道题,里面就隐含着这么多坑,让我对后面中等难度及复杂难度有一种未知的恐惧感.Anyway,反正已经开始入坑了,希望自己能坚持下去,尽量抽时间刷一刷题吧.
最后,总结一下,刷题新手从这题得到的经验是:以后做题,首先闲考虑几种边界情况,数组为空,元素为空,及元素数量少该怎么处理.这样就能省好多事,嗯,记录一下这个心路历程.好了,去看前面的人的解法去了,研究下,看看别人的解法什么样,学习一下