小哆啦解题记:字符串里的 “捉迷藏”
小哆啦开始刷力扣的第十九天
难题降临
在编程小镇里,小哆啦可是个热爱挑战的编程小能手,整天都在代码的世界里探险。有一天,编程精灵小智急匆匆地找到了小哆啦,气喘吁吁地说:“小哆啦,大事不好啦!编程魔法世界出难题啦,得靠你去解开。”
小哆啦一听,眼睛瞬间亮闪闪的,兴奋地搓搓手:“啥难题呀,快说来听听,我小哆啦出马,肯定没问题!”
小智清了清嗓子,说道:“现在有两个字符串 haystack 和 needle,要在 haystack 里找到 needle 第一次出现的位置,要是找不到,就返回 -1。这就好像 needle 在 haystack 里玩捉迷藏,你得把它第一次藏的位置找出来哟。”
初次尝试,遭遇挫折
小哆啦拍着胸脯保证:“这有啥难的,看我的!” 说完,就噼里啪啦地在键盘上敲起了代码:
function strStr(haystack: string, needle: string): number {
let hayArr = haystack.split();
let neeArr = needle.split();
for (let i = 0; i < hayArr.length; i++) {
let num = i;
let temp = 0
while(hayArr[num] == neeArr[temp]){
num ++
temp ++
if(temp == neeArr.length -1 ){
return num
}
}
}
return -1
};
小哆啦满心欢喜地运行代码,结果却像被泼了一盆冷水,输出全是错的。小哆啦挠着脑袋,急得像热锅上的蚂蚁,嘴里嘟囔着:“咋回事呀,不应该呀。”
错误排查,逐步修正
这时,小智在旁边笑着提醒:“小哆啦,你看哈,split 方法要是不传入参数,它就会把整个字符串当成一个整体放到数组里,根本不会按字符分割哟。就好像你要把一堆糖果一颗一颗分开,结果没说清楚,糖果还是整包在一起呢。”
小哆啦恍然大悟,赶紧修改代码:“原来是这样,我得传入空字符串 '' 才行。”
改完之后,小哆啦又发现返回值的逻辑不对。小智笑着拍拍小哆啦的肩膀说:“你在 while 循环里,当 temp 等于 neeArr.length - 1 就返回 num,这可不对呀。得等 temp 等于 neeArr.length 时,才说明整个 needle 都找到了,这时候要返回 i,也就是 haystack 里开始匹配的位置。就好比你找宝藏,得找到完整的宝藏才算数,不能只找到一半就说找到了呀。”
小哆啦不好意思地吐吐舌头,再次修改代码,同时还处理了 needle 为空字符串的边界情况。修改后的代码如下:
function strStr(haystack: string, needle: string): number {
if (needle === '') {
return 0;
}
let hayArr = haystack.split('');
let neeArr = needle.split('');
for (let i = 0; i <= hayArr.length - neeArr.length; i++) {
let num = i;
let temp = 0;
while (num < hayArr.length && temp < neeArr.length && hayArr[num] === neeArr[temp]) {
num++;
temp++;
}
if (temp === neeArr.length) {
return i;
}
}
return -1;
}
这次运行代码,结果终于正确啦。
寻求优化,邂逅 KMP
不过,小智又发话了:“小哆啦,你这个方法虽然能解决问题,但是当字符串很长的时候,效率就有点低啦。就好比你在一个超级大的迷宫里找东西,一个一个房间地找,速度肯定慢。我们可以用更厉害的 KMP 算法来优化。”
小哆啦一听,眼睛里闪着好奇的光:“KMP 算法?听起来好厉害,快给我讲讲。”
小智耐心地解释:“KMP 算法呀,就像是有一个神奇的地图,能让你在找 needle 的时候,跳过一些已经检查过的地方。我们先对 needle 做个预处理,算出一个 next 数组,这个数组能记录 needle 里每个位置前面子串的最长公共前后缀长度。当匹配的时候遇到不匹配的情况,就根据 next 数组移动 needle,这样就能少做很多比较啦。”
成功优化,收获成长
小哆啦认真地听着,然后在小智的帮助下,写出了使用 KMP 算法的代码:
function strStr(haystack: string, needle: string): number {
if (needle === '') {
return 0;
}
const m = haystack.length;
const n = needle.length;
const next = computeNext(needle);
let i = 0;
let j = 0;
while (i < m) {
if (needle[j] === haystack[i]) {
i++;
j++;
}
if (j === n) {
return i - j;
} else if (i < m && needle[j] !== haystack[i]) {
if (j !== 0) {
j = next[j - 1];
} else {
i++;
}
}
}
return -1;
}
function computeNext(needle: string): number[] {
const n = needle.length;
const next: number[] = new Array(n).fill(0);
let len = 0;
let i = 1;
while (i < n) {
if (needle[i] === needle[len]) {
len++;
next[i] = len;
i++;
} else {
if (len !== 0) {
len = next[len - 1];
} else {
next[i] = 0;
i++;
}
}
}
return next;
}
小哆啦运行优化后的代码,速度明显快了很多,兴奋得跳了起来:“哇,KMP 算法太厉害啦,就像给我的代码装上了火箭发动机!”
结语
经过这次挑战,小哆啦深刻体会到了编程世界的奇妙与深邃。每一次错误都是成长的基石,每一次优化都是向更高峰的攀登。在编程小镇里,小哆啦的名气随着这次成功的挑战越来越大,但他知道,这只是漫漫编程路上的一小步。未来,还会有更多复杂的难题、更神奇的算法等待着他去探索。小哆啦怀揣着对编程的热爱与执着,期待着下一次充满惊喜与挑战的编程冒险,因为他坚信,在代码的星辰大海中,总有新的宝藏等待他去发现!