实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll" 输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba" 输出: -1
说明:
当 needle
是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle
是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
import java.util.Arrays;
class Solution {
/*
* 思路一:调用库函数
* 执行用时 :1 ms, 在所有 Java 提交中击败了99.35%的用户
* 内存消耗 :37.5 MB, 在所有 Java 提交中击败了45.06%的用户
*/
public static int strStr0(String haystack,String needle)
{
return haystack.indexOf(needle);
}
/* 思路二:暴力法,时间复杂度O((haystack.length-needle.length)needle.length)
* 执行用时 :3 ms, 在所有 Java 提交中击败了51.35%的用户
* 内存消耗 :36.8 MB, 在所有 Java 提交中击败了77.42%的用户
*/
public static int strStr1(String haystack, String needle) {
if(needle.isEmpty()) return 0;
int m=haystack.length(),n=needle.length();
if(m<n) return -1;
for(int i=0;i<=m-n;++i) {
int j=0;
for(j=0;j<n;++j) {
if(haystack.charAt(i+j)!=needle.charAt(j)) break;
}
if(j==n) return i;
}
return -1;
}
/*
* 思路三: KMP算法
* 执行用时 :4 ms, 在所有 Java 提交中击败了42.83%的用户
* 内存消耗 :36 MB, 在所有 Java 提交中击败了83.78%的用户
*/
public static int strStr2(String haystack,String needle)
{
if(needle==null||needle.length()==0)
return 0;
if(haystack==null||needle==null||haystack.length()<1||needle.length()<1)
return -1;
//主字符串haystack的下标
int indexT=0;
//模式串needle的下标
int indexP=0;
char[] charT=haystack.toCharArray();
char[] charP=needle.toCharArray();
//获得nexts数组
int[] nexts=getNexts(charP);
while(indexT<charT.length && indexP<charP.length) {
if(charT[indexT]==charP[indexP])
{
indexT++;
indexP++;
}else if(nexts[indexP]==-1)//如果第一位都不匹配,则直接下一个字符
indexT++;
else
indexP=nexts[indexP];
}
return indexP==charP.length?indexT-indexP:-1;
}
//获得next数组
private static int[] getNexts(char[] str) {
if(str.length==1)
return new int[] {-1};
int[] nexts=new int[str.length];
nexts[0]=-1;
nexts[1]=0;
//指向当前元素最长前缀的值
int cn=0;
//遍历字符串时的下标
//ababcababe,[-1,0,0,1,2,0,1,2,3,4]
int index=2;
System.out.println("index "+" cn "+" case");
while(index<str.length) {
System.out.print(" "+index+" ");
System.out.print(cn+" ");
if(str[index-1]==str[cn])
{
nexts[index++]=++cn;
System.out.print("str[index-1]==str[cn]");
}
else if(cn>0)
{
cn=nexts[cn];
System.out.print("cn>0");
}
else
{
nexts[index++]=0;
System.out.print("else");
}
System.out.println();
}
return nexts;
}
public static void main(String[] args) {
String haystack="aaababcababea",needle="ababcababe";
System.out.println("the result"+strStr2(haystack,needle));
}
}
字符串的模式匹配是对字符串的基本操作之一,应不断地改良模式匹配算法,减少其时间复杂度。我们介绍下KMP算法,通过运用对这个词在不匹配时本身就包含足够信息来确定下一个匹配将在哪里开始,从而避免重新检查先前匹配的字符,避免i回溯,极大提高算法效率。
KMP算法,让前面匹配的结果指导后面开始的匹配
1.匹配过程
利用“已部分匹配”这个有效信息,保持i指针不回溯,修改j指针
匹配失败时j要移动的下一位满足
证明:当
2.求模式串needle的nexts数组
(1)知乎上有个例子,很通俗的做了讲解。
我也大概说下,前缀:从第一个元素开始不包含末尾元素的子串;后缀:从最后一个元素开始不包含头元素的子串。
主串:abbaabbaaba
模式串:abbaaba
abbaab 的头部有 a, ab, abb, abba, abbaa(不包含最后一个字符,前缀) abbaab 的尾部有 b, ab, aab, baab, bbaab(不包含第一个字符,后缀) 这样最长匹配是 ab,模式串回退到第三个字符和主串继续匹配。
(2)个人通俗理解KMP算法求模式串needle的nexts数组,是一个找字符串中重合的算法,一个字符k的nexts值表示其前一个字符是否与一直串重复,重复则nexts[k]=nexts[k-1]+1;不重复则根据当前nexts值(cn)采取置零或赋值操作
(3)我们用字符串"ababcababe"讲一个例子:
"ababcababe"的nexts数组为"-1001201234",运行我给出的代码可以得到下表,也就是具体的迭代方法