LeetCode-28.实现strStr()之追逐与逃逸
2024-01-13 00:51:24
金三银四,一年一度的跳槽季又到了,刷题的重要性自然不必多言。与其说是刷题拿offer,不如说是为自己在技术行业沉淀知识厚度和技术深度,不断夯实自己技术地基的好时机。
文字与程序殊途同归
今天我们不妨将目光投向字符串处理上,一道经典的题目便是LeetCode-28.实现strStr()。这道题的关键是如何在给定的haystack字符串中,找到目标字符串needle第一次出现的位置。解决这道题,如同在进行一场追逐与逃逸的游戏,既需要追击者发挥主动性,也需要被追击者表现得"狡猾"。
正序匹配,层层递进
追击者自然是由我们来扮演的。假设haystack字符串中有n个字符,needle字符串中有m个字符,则理论上我们需要进行n-m+1次匹配尝试。而每次尝试,我们都需要比较needle中的所有字符,这又涉及到m次字符比较。粗略计算一下,这样的时间复杂度达到O(n*m)。
当然,现实远比理论要复杂。正序匹配的过程,可以理解为两条指针的同步推进,一条指向haystack字符串,另一条指向needle字符串。在每一步匹配中,只有两条指针指向的字符相同时,才继续向后匹配;反之,则重新对齐needle字符串,继续匹配。这一过程中,我们将依次获得两个字符相等的连续序列,直至序列的长度等于m,此时needle字符串在haystack字符串中的起始位置便确定了。
KMP算法,巧夺先机
虽然正序匹配的过程比较简单,但效率并不高。为了缩短匹配时间,便有了KMP算法的出现。这一算法的核心思想在于:在正序匹配过程中,若当前字符比较失败,则不需要重新对齐needle字符串,而是根据当前的失配信息,直接跳到needle字符串中下一个可能匹配的位置上。
这种巧夺先机的策略,不仅提高了匹配效率,同时也简化了匹配逻辑。在KMP算法中,失配信息被存储在一个名为next数组中,该数组的长度为m+1,其中next[i]的值表示,当needle字符串中第i个字符与haystack字符串中当前匹配的字符不匹配时,needle字符串中下一个可能匹配的字符是第next[i]个字符。
实践出真知,动手小试牛刀
理论讲起来固然容易,下面我们用一段代码来实战一下KMP算法。
public int strStr(String haystack, String needle) {
if (needle.isEmpty()) {
return 0;
}
int[] next = getNextArray(needle);
int i = 0, j = 0;
while (i < haystack.length() && j < needle.length()) {
if (haystack.charAt(i) == needle.charAt(j)) {
i++;
j++;
} else if (j > 0) {
j = next[j];
} else {
i++;
}
}
return j == needle.length() ? i - j : -1;
}
public int[] getNextArray(String needle) {
int[] next = new int[needle.length() + 1];
next[0] = -1;
int i = 0, j = -1;
while (i < needle.length()) {
if (j == -1 || needle.charAt(i) == needle.charAt(j)) {
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
return next;
}
这段代码中,getNextArray()方法用于生成next数组,strStr()方法则实现了KMP算法的匹配过程。
涓滴积累,铸就技术之基
这道LeetCode题虽小,但其中蕴藏的算法思想却十分巧妙。通过不断练习这类题目,不仅可以提升我们的算法能力,更重要的是潜移默化地培养我们的思维方式,让我们在解决问题时变得更加灵活和富有创造性。
加油吧,算法之路虽然坎坷,但相信只要坚持不懈,终能到达彼岸。