今天只有两道题, 而且是简单题, 但是一点都不简单. 因为我们学习了一种KMP算法, 而且需要掌握这个模板。
题目链接
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
方法一, 使用Java内置函数
class Solution {
public int strStr(String haystack, String needle) {
return haystack.indexOf(needle);
}
}
方法二, 暴力循环法
时间复杂度 O ( M ∗ N ) O(M *N) O(M∗N)
class Solution {
public int strStr(String haystack, String needle) {
int hLen = haystack.length(), neLen = needle.length();
for(int i = 0; i<= hLen - neLen; i++){
if(needle.charAt(0) == haystack.charAt(i)){
int left = 0;
int right = i;
while(left < neLen && needle.charAt(left) == haystack.charAt(right)){
left++;
right++;
}
if(left == needle.length()){
return i;
}
}
}
return -1;
}
}
方法三,使用KMP方法
时间复杂度 O ( M + N ) O (M + N) O(M+N)
第一步: 用一个Next数组来记录最长公共前后缀的长度
如何理解这个最长公共前后缀?
String s = “aabaa”
它的前缀包括(所有包含头部不包含尾部的子串): a, aa, aab, aaba
他的后缀包括(所有包含尾部不包含头部的子串): a, aa, baa, abaa (注意顺序都是从左到右, 不是回文串)
最长的公共前后缀为 “aa”, 长度为2
那么 字符串 S = “aabaaf”
对应的Next 数组为 [0, 1, 0, 1, 2, 0]
第二步, 运用Next来查询, 可以当作模板来记住, 中间还用了另外一种KMP的方法,但是没有这个好理解。
class Solution {
public int strStr(String haystack, String needle) {
if(needle.isEmpty()) return 0;
// get next array
int[] next = new int[needle.length()];
int j = 0;
next[0] = 0;
for(int i = 1; i < needle.length(); i++){
while(j>0 && needle.charAt(i) != needle.charAt(j)){
j = next[j-1];
}
if(needle.charAt(i) == needle.charAt(j)){
j++;
}
// eventually j will equal to 0
next[i] = j;
}
//from next array to match haystack
j = 0;
for(int i = 0; i< haystack.length(); i++){
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;
}
}
方法四
KMP变种
class Solution {
public int strStr(String haystack, String needle) {
if(needle.isEmpty()) return 0;
int[] LPS = new int[needle.length()];
int prevLPS = 0, i = 1;
// create the Longest prefix suffix array
while(i < needle.length()){
if(needle.charAt(i) == needle.charAt(prevLPS)){
LPS[i] = prevLPS + 1;
prevLPS++;
i++;
}else if(prevLPS == 0){
LPS[i] = 0;
i++;
}else{
prevLPS = LPS[prevLPS -1];
}
}
i = 0; // pointer for haystack
int j = 0; // pointer for needle
while(i < haystack.length()){
if(haystack.charAt(i) == needle.charAt(j)){
i++;
j++;
}else{
if(j == 0){
i ++;
}else{
j = LPS[j -1];
}
}
if(j == needle.length()){
return i - needle.length();
}
}
return -1;
}
}
题目链接
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
方法一
暴力循环法,时间复杂度 O ( N 2 ) O(N^2) O(N2)
class Solution {
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
for(int i =1; i <= len / 2; i++){
if(len % i != 0){
continue;
}
String part = s.substring(0, i);
for(int j = i; j<=len -i; j+= i){
String next = s.substring(j, j+i);
if(!next.equals(part)){
break;
}
if(j + i == len){
return true;
}
}
}
return false;
}
}
方法二
使用KMP中的Next数组, 如果一个字符串由相同的重复的子字符串组成, 那么他们的最大的公共前后缀和原数组相差的,一定就是重复的字符串。
比如 S = “abcabc" Next[] = [0, 0, 0, 1, 2, 3]
最大公共前后缀为 abc, 长度为3.
6 % ( 6 - 3) = 0
所以 len % (len - 最大的公共前后缀长度) = 0
满足该条件说明是由重复字符串组成, 否则则不是
class Solution {
public boolean repeatedSubstringPattern(String s) {
int[] next = new int[s.length()];
int j = 0;
next[0] = 0;
for(int i = 1; i<s.length(); i++){
while(j>0 && s.charAt(i) != s.charAt(j)){
j = next[j-1];
}
if(s.charAt(j) == s.charAt(i)){
j++;
}
next[i] = j;
}
int len = s.length();
if(next[len -1] != 0 && len % (len - next[len -1]) == 0){
return true;
}else{
return false;
}
}
}