所有代码 java
实现strStr() LeetCode 28
题目链接:实现strStr() LeetCode 28 - 中等
思路
首选暴力,然后是字符串的匹配题经典的KMP算法(这个不太会,可以看代码随想录)
暴力解法
public int strStr(String haystack, String needle) {
int n = haystack.length();
int m = needle.length();
// 原串开始进行匹配
for (int i = 0; i <= n - m; i++) {
int a = i; // 原串匹配位置
int b = 0; // 子串匹配位置
while (b < m && haystack.charAt(a) == needle.charAt(b)){
// 匹配成功往后移匹配下一个字母
a++;
b++;
}
// 子串完全匹配成功
if (b == m) {
return i;
}
}
// 没有匹配成功
return -1;
}
KMP算法
KMP算法最关键的是维护一个next的数组,这个next数组存的就是每次匹配开始的下标,有了这个next数组每次匹配就不用从头开始,极大的节省了时间。
求next数组分为四步
- next数组初始化;
- 前后缀相同的情况处理逻辑;
- 前后缀不同的情况处理逻辑;
- next数组更新
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
int[] next = new int[needle.length()];
getNext(next,needle);
// j 指向子串,i指向模式串
int j = 0;
for (int i = 0; i < haystack.length(); i++) {
// 该项未匹配成功就找j的前项 最长相等前缀的位置
while (j > 0 && haystack.charAt(i) != needle.charAt(j)){
j = next[j-1];
}
// 匹配成功就往后移一位
if (haystack.charAt(i) == needle.charAt(j)){
j++;
}
if (j == needle.length()){
return i - needle.length() + 1;
}
}
return -1;
}
public void getNext(int[] next, String s){
// j指向前缀末尾位置,也是最长相同前后缀的长度
int j = 0;
next[0] = 0;
// i指向后缀末尾位置
for (int i = 1; i < s.length(); i++) {
while (j > 0 && s.charAt(i) != s.charAt(j)){
// next[j]记录的是最长相同的前后缀
j = next[j-1];
}
if (s.charAt(i) == s.charAt(j)){
j++;
}
// 更新next数组
next[i] = j;
}
}
总结
暴力简单,时间复杂度要高一点O(n*m),实在不会用暴力能过一点就一点了。但是KMP时间复杂度只有O(m+n),自己还需要深入的理解,多看几遍。
重复的子字符串 LeetCode 459
题目链接:重复的子字符串 LeetCode 459 - 简单
思路
首先想到的也是暴力,其次是KMP
暴力
public boolean repeatedSubstringPattern(String s) {
StringBuilder sb = new StringBuilder();
int len = s.length();
// 字符一定是从第一个字母开始, 且超过一半就不会是子串了 - 剪枝
for (int i = 0; i < len / 2; i++){
sb.append(s.charAt(i));
// 取的子串并不能被整除,就不能构成子串 - 剪枝
if (len % sb.length() != 0){
continue;
}else {
int div = len / sb.length();
StringBuilder sc = new StringBuilder();
// 判断要重复几次
for (int j = 0; j < div; j++) {
sc.append(sb);
}
// System.out.println(sc.toString() == s); 字符串的==判断的是地址,需用equal
if (sc.toString().equals(s)){
return true;
}
}
}
return false;
}
KMP
难!!!
public boolean repeatedSubstringPattern(String s) {
if (s.equals("")) return false;
int len = s.length();
char[] chars = s.toCharArray();
int[] next = new int[len]; // 初始所有元素为0
// 构造 next 数组过程,j从0开始(空格),i从2开始
int j = 0;
for (int i = 1; i < len; i++) {
// 匹配不成功,j回到前一位置 next 数组所对应的值
while (j>0 && chars[i] != chars[j]){
j = next[j-1];
}
// 匹配成功,j往后移
if (chars[i] == chars[j]){
j++;
}
// 更新 next 数组
next[i] = j;
}
// 最后判断是否是重复的子字符串,
// 这里next[len-1]即代表next数组末尾的值
// len % (len - next[len-1] 表示重复了几次,并且能整除
if (next[len - 1] > 0 && len % (len - next[len - 1]) == 0) {
return true;
}
return false;
}
总结
我们还可以把字符串重复一遍,再到其中搜索去除首尾字母能不能找到原字符串,找到则是重复构成的。如:abcabc,重复一遍abcabcabcabc,中间一定有原字符串,注意:要去掉首尾的字母,不然就找到自己了!漂亮的一行写完
return (s+s).indexOf(s,1) != s.length();