摘要
本文主要介绍了KMP算法,并提供了LeetCode中28. 实现 strStr()、459. 重复的子字符串题目的暴力解法。最后,我们进行了字符串算法的总结,并回顾了双指针技巧。
1、KMP算法
KMP(Knuth-Morris-Pratt)算法是一种用于字符串匹配的高效算法,用于在一个主文本字符串中查找一个较短的子串。它的主要思想是通过预处理模式字符串(要查找的子串),构建一个部分匹配表(也称为最长前缀后缀表),然后使用这个表来在主文本字符串中跳过不必要的比较,从而加速字符串匹配的过程。
KMP算法的关键是构建部分匹配表,它在模式字符串中查找每个前缀(包括自身)的最长前缀后缀的长度。这个表告诉我们,当在主文本中发生不匹配时,应该将模式字符串向右移动多少个位置,以便继续比较。
KMP算法的步骤如下:
- 预处理模式字符串,构建部分匹配表。
- 在主文本字符串中从左到右扫描,比较主文本和模式字符串的字符。
- 当发生不匹配时,根据部分匹配表的值来移动模式字符串,而不是回溯主文本字符串的指针。
- 重复步骤2和步骤3,直到找到匹配或者遍历完主文本字符串。
KMP算法的优点是它避免了在不匹配时不必要的字符比较,从而提高了匹配效率。这使得KMP算法在大文本中查找子串时表现出色。
总的来说,KMP算法是一种经典的字符串匹配算法,用于在时间复杂度为O(N+M)的情况下,在一个长文本中高效地查找一个模式串。
2、28. 实现 strStr()
2.1 暴力法
思路
暴力法:双层遍历,外层遍历haystack,从下标0开始到(len1-len2),内层遍历needle, 比较haystac下标i+j 的元素与needle下标j 的元素, 如果匹配直到needle结束,则返回下标i,如果不匹配,内层遍历break
代码
public int strStr(String haystack, String needle) {
int len1 = haystack.length();
int len2 = needle.length();
for(int i=0; i<len1-len2+1; i++) {
for(int j=0; j<len2; j++) {
if(haystack.charAt(i+j) != needle.charAt(j)) {
break;
}
if(j == len2-1) {
return i;
}
}
}
return -1;
}
2.2 KMP *
3、459.重复的子字符串
3.1 暴力法
思路
暴力法,外层遍历,i代表子串的大小,字符串s 是字串的倍数才进行内层遍历,定义j从i开始遍历至结束 如果s.charAt(j) != s.charAt(j-i) 则代表不是重复的字符串,内存循环break
s.charAt(j) != s.charAt(j-i)
代码
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
for(int i=1; i<=len/2; i++) {
if(len % i == 0) {
boolean flag = true;
for(int j=i; j<len; j++) {
if(s.charAt(j) != s.charAt(j-i)) {
flag = false;
break;
}
}
if(flag) {
return true;
}
}
}
return false;
}
3.2 KMP *
4、字符串总结
以下是对字符串算法专题的总结,包括各个算法题的主要思路和关键点:
-
344. 反转字符串
- 使用双指针法,一个指向字符串的开头,一个指向结尾,不断交换字符直至遍历完整个字符串。
-
541. 反转字符串 II
- 基于反转字符串的思路,但这次要求只反转每个2k个字符中的前k个字符,保留其余字符不变。
-
剑指Offer 05. 替换空格
- 遍历原字符串,将空格替换为"%20"。
-
151. 翻转字符串里的单词
- 先翻转整个字符串,再依次翻转每个单词。
-
剑指Offer58-II. 左旋转字符串
- 将字符串分为两部分,分别翻转,然后合并翻转后的字符串。
-
28. 实现 strStr()
- 使用KMP算法或暴力匹配算法,找出目标字符串在原字符串中的位置。
-
459. 重复的子字符串
- 将原字符串复制一份,去掉首尾字符,如果复制后的字符串中仍包含原字符串,说明原字符串是由一个子字符串重复多次构成。
这些算法题涵盖了字符串处理中的常见问题,包括反转、替换、翻转单词、子字符串匹配等。总结时可以强调不同题目的不同解决思路,例如使用双指针、KMP算法、复制拼接等。此外,注意字符串问题的常见技巧和注意事项,如处理边界情况、字符串为空等。
5、双指针回顾
数组篇:
- 使用双指针法能有效提高数组操作的效率,尤其在移除元素时。
- 避免使用erase操作,因为它的时间复杂度是O(n),使用双指针可以将操作变成O(n)。
字符串篇:
- 双指针法可用于反转字符串、替换空格、删除冗余空格等操作,时间复杂度为O(n)。
- 从后向前填充字符串可以提高效率,避免O(n^2)的算法。
链表篇:
- 使用双指针法翻转链表,只需改变next指针的指向,无需重新定义新链表。
- 快慢指针是判断链表是否有环的经典方法,可用于找到环的入口。
N数之和篇:
- 哈希法适用于两数之和问题,但不适用于三数之和等问题,因为去重和剪枝困难。
- 使用双指针法可以将时间复杂度从O(n^2)降低到O(n)。
总结:
- 本文介绍了九道LeetCode题目,展示了双指针法的应用。
- 建议练习这些题目,熟练掌握双指针法,以提高算法解题效率。