kmp,多k几次就好了。(结合简单例子比较容易理解)
熟读力扣300题,不会写题也会敲。
首先说一说,最长公共前后缀
比如abc,前缀不包含最后一个字符的所有以第一个字符开头的连续子串,前缀a,ab;
同理后缀,c,bc;
下面就是字符串的next数组,对应的值就是公共前后缀长度。
eg:abcabc
前缀:a,ab,abc,abca,abcab
后缀:c,bc,abc,cabc,bcabc
所以最长公共前后缀的长度是3。
上面是手动推出来的3,那用代码怎么写呢
j:保存最长公共前后缀的长度
i:当前字符串的下标
s.charAt(i)==s.charAt(j)
第一种,如果当前字符和j下标字符相同,可以通过前一个推导出来,
即next[i]=next[i-1]+1;又因为next[i-1]=j;所以next[i]=j+1;
有种dp的思想。
=
while (s.charAt(i)!=s.charAt(j) && j>0){
j=next[j-1];
}
第二种,这种情况下,j的下标要变为j前一个的next数组值,也就是回退。回退到哪里呢?因为当前s.charAt(i)!=s.charAt(j),其实想找到s.charAt(i)==s.charAt(j),这样就可以递推了。所以j要回退,也就是j=next[j-1],如果s.charAt(i)!=s.charAt(j),继续回退,直到j=0;
下面这个例子:
此时不相等,j要往前回退
这时s.charAt(i)==s.charAt(j),又可以用第一种的思路了。
求next数组的完整代码
public static void next(int []next,String s){
next[0]=0;
int j=0;
for(int i=1;i<s.length();i++){
while (s.charAt(i)!=s.charAt(j) && j>0){
j=next[j-1];
}
if(s.charAt(i)==s.charAt(j))
j++;
next[i]=j;
}
}
对应的流程:
next数组求出来,怎么进行字符串匹配呢
leetcode28. 实现 strStr()
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
其实kmp的思想就是,主串的下标不回退,只回退模式串,这就是是不同于暴力的方法。
eg: 此时i和j不匹配,如果是暴力的情况下,i要回退到b的位置
如果是用next数组,只需要把j移动到next[j-1]的位置,再进行比较
此时i和j还不匹配,继续回退
j回退到0,i往后移动
最终完成匹配。
28. 实现 strStr()
public static int strStr(String ss, String pp){
if(ss.length()==0||pp.length()==0)
return -1;
int []next = new int[pp.length()];
next(next,pp);
for(int i=0,j=0;i<ss.length();i++){
while(j>0&&ss.charAt(i)!=pp.charAt(j))
j=next[j-1];
if(ss.charAt(i)==pp.charAt(j))
j++;
if(j==pp.length())
return i-j+1;
}
return -1;
}
//abcabcabcd (j=0,i=1开始,j是最长公共前缀和的长度)
public static void next(int []next,String s){
next[0]=0;
int j=0;
for(int i=1;i<s.length();i++){
while (s.charAt(i)!=s.charAt(j) && j>0){
j=next[j-1];
}
if(s.charAt(i)==s.charAt(j))
j++;
next[i]=j;
}
}
- 重复的子字符串
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成
输入: s = “abab”
输出: true
解释: 可由子串 “ab” 重复两次构成
这个题不是很容易想到kmp
举个例子:
可以清楚的看到包含重复的字符串,
字符串的长度=next[最后一个字符下标]+重复字符串的长度
有点意思。。
上菜。。。。
public static boolean repeatedSubstringPattern(String s){
if(s.length()==1)
return true;
int []next =new int[s.length()];
getnext(s,next);
//还要判断一下最后一个next数组的值,如果为0,肯定不包含重复字符串
if(s.length()%(s.length()-next[s.length()-1])==0 && next[s.length()-1]!=0)
return true;
return false;
}
public static void getnext(String s , int []next){
int j = 0;//保存最长前后缀长度
next[0]=0;
for(int i = 1;i<s.length();i++){
while (s.charAt(i)!=s.charAt(j) && j>0)
j=next[j-1];
if(s.charAt(i)==s.charAt(j))
j++;
next[i]=j;
}
}