文章目录
28.实现strStr()
459.重复的子字符串
这两道题先了解一下算法原理,不打算细抠,二刷再把java实现代码补上
1.【28】实现strStr()
1.1 题目描述
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
示例 1:
输入:haystack = “sadbutsad”, needle = “sad”
输出:0
解释:“sad” 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = “leetcode”, needle = “leeto”
输出:-1
解释:“leeto” 没有在 “leetcode” 中出现,所以返回 -1 。
提示:
- 1 <= haystack.length, needle.length <= 104
- haystack 和 needle 仅由小写英文字符组成
1.2 解题思路 (在一个串中查找是否出现过另一个串,这是KMP的看家本领)
KMP算法相关知识:KMP算法讲解
1.3 java代码实现
- 时间复杂度: O(n + m)
- 空间复杂度: O(m), 只需要保存字符串needle的前缀表
class Solution {
public void getNext(int[] next, String s){
int j = -1;
next[0] = j;
for (int i = 1; i < s.length(); i++){
while(j >= 0 && s.charAt(i) != s.charAt(j+1)){//前后缀不相同
j=next[j];//向前回退
}
if(s.charAt(i) == s.charAt(j+1)){
j++;
}
next[i] = j;
}
}
public int strStr(String haystack, String needle) {
if(needle.length()==0){
return 0;
}
int[] next = new int[needle.length()];
getNext(next, needle);
int j = -1;//因为next数组里记录的起始位置为-1
for(int i = 0; i < haystack.length(); i++){//注意i就从0开始
while(j>=0 && haystack.charAt(i) != needle.charAt(j+1)){//不配
j = next[j];//j寻找之前匹配的位置
}
if(haystack.charAt(i) == needle.charAt(j+1)){//匹配,j和i同时向后移动
j++;//i的增加在for循环里
}
if(j == needle.length()-1){
return (i-needle.length()+1);//文本串s里出现了模式串t
}
}
return -1;
}
}
2.【459】重复的子字符串
2.1 题目描述
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = “abab”
输出: true
解释: 可由子串 “ab” 重复两次构成。
示例 2:
输入: s = “aba”
输出: false
示例 3:
输入: s = “abcabcabcabc”
输出: true
解释: 可由子串 “abc” 重复四次构成。 (或子串 “abcabc” 重复两次构成。)
提示:
- 1 <= s.length <= 104
- s 由小写英文字母组成
2.2 解题思路
2.3 java代码实现
3.字符串总结
划重点!!!
双指针法是字符串处理的常客。
KMP算法是字符串查找最重要的算法,但彻底理解KMP并不容易
3.1 什么是字符串
字符串是若干字符组成的有限序列,也可以理解为是一个字符数组
3.2 要不要使用库函数
【建议】
- 如果题目关键部分直接使用库函数就能解决,建议不要使用库函数
- 如果库函数仅仅是解题过程中的一小部分,并且你已经清楚这个库函数的内部实现原理的话,可以考虑使用库函数
3.3 双指针法
双指针法在数组、链表和字符串中很常用。
- 在344.反转字符串中,使用双指针法实现了反转字符串的操作。
- 在LCR122.路径加密中,使用双指针法在时间复杂度为O(n)的情况下完成替换空格。
很多数组填充类的问题,都可以预先给数组扩容至带填充后的大小,然后从后向前操作 。
- 针对数组删除操作的问题,在27.移除元素中已经使用了双指针法进行移除操作。
- 同样的道理,在151.反转字符串里的单词中使用O(n)的时间复杂度,完成了删除冗余空格操作。
3.4 反转系列
在541.反转字符串II中,我们能想到的暴力解法就是:对于每隔2k个字符的前k个的字符,写了一堆逻辑代码或者再写一个计数器,来统计2k,再统计前k个字符
其实当需要固定规律一段一段的去处理字符串的时候,要想想在for循环的表达上做做文章
只要让i += 2k,i每次移动2k就可以了,然后判断是否需要有反转的区间
因为要找的也就是每2*k区间的起点,这样程序会高效很多
在151.反转字符串里的单词中要求翻转字符串里的单词,这道题综合考察了字符串的多种操作
这道题目通过先整体反转再局部反转,实现了反转字符串里的单词。
反转字符串还有一个牛逼的用处,就是达到左旋的效果。
在LCR182.动态口令中,通过先局部反转再整体反转达到了左旋的效果
3.5 KMP
KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
KMP的精髓所在就是前缀表,在KMP算法相关知识中讲解了什么是KMP,什么是前缀表,什么时候使用前缀表
前缀表:起始位置到下标i之前(包括i)的子串中,有多大长度的相同前后缀
使用KMP可以解决两类经典问题:
- 匹配问题 28.实现strStr()
- 重复子串问题459.重复的子字符串
前缀:指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀:指不包含第一个字符的所有以最后一个字符结尾的连续子串。
然后针对前缀表到底要不要减一,这其实是不同KMP实现的方式,在KMP算法相关知识中针对之前两个问题,分别给出了两个不同版本的的KMP实现。
其中主要理解j=next[x]这一步最为关键!
4.双指针回顾
请看文章:双指针总结篇