KMP算法
给定两个字符串S1,S2,其长度分别为M和N,且M>N;判断S1中是否存在S2,若存在则返回S2在S1中第一个字符的索引位置,否则直接返回-1。
首先获取子串的最长前缀与最长后缀数组每个数组的数值分别与子串中每个字符相对应,表示该字符最长前缀与最长后缀的大小,并且最长前缀不能包括最后一个字符,最长后缀不能包括第一个字符;对于一个字符而言,第一个字符对应的数值为-1,第二个数值对应为0,整体从索引为2的位置开始进行计算。对当前字符而言,若其后一个字符与后一个字符对应的最长前缀的下一个字符相等,则当前字符最长前缀与最长后缀数值等于前一个字符的数值加1,否则若两者不相等,在前一个字符的最长前缀不为0的情况下,跳到下一个字符的位置然后与当前字符进行比较,若一直到最后都依然不匹配,则将当前字符的数值赋值为0;获取上述子串的数组之后,接下来便是依次对两个字符串对应数值进行比较,分别定义两个指针M,N指向两个字符数组,当两个指针指向的字符相等时,则将两个指针前移一位,当两个字符不相等时,若当前子串字符对应位置的最长前缀与最长后缀数值为-1,则说明此时子串已经位于第一个字符的位置,已经没法再次移动,则将长串的字符位置前移一位,否则将子串移动到当前不等字符对应的最长前缀的位置再次与之前不等的长串的字符进行比较。
- 首先获得子串每个位置对应的最长前缀以及后缀数组
public static int[] getIndex(char[] chr)
{
if(chr.length<1)
{
return new int[]{-1}; ///若当前字符数组的长度小于1,则直接返回-1
}
int[] index = new int[chr.length]; ///定义一个与字符数组长度相等的索引数组
index[0] = -1;
index[1] = 0; ///前两个字符索引为固定数值
int i = 2; 从第二个索引的位置开始进行计算
int cn= 0; ///这里的cn表示的当前索引位置的最长前缀后缀数的后一个字符的位置
while(i<index.length)
{
if(chr[i-1]==chr[cn]) ///若当前字符的前一个字符的位置与前一个字符最长前缀与后缀数的位置相等,则直接将当前最长前缀以及后缀的数值加1
{
index[i++] = ++cn;
}
else if(cn>0) ///到这里说明两个数值不相等,则将cn的数值向前推进,再次进行比较
{
cn = chr[cn];
}
else 若达到这里说明此时cn也已经不能再向前推进,直接将当前字符索引的位置赋值为0
{
index[i++] = 0;
}
}
return index; 返回子串的最长前缀与最长后缀数组
}
- 主函数判断在长串中是否存在子串,若存在则返回子串在主串中第一个索引的位置
public static int KMPAlgorithm(String s1,String s2)
{
if(s1==null||s2==null||s2.length()<1||s1.length()<s2.length())
{
return -1;
}
char[] chr1 = s1.toCharArray();
char[] chr2 = s2.toCharArray();
int j1 = 0;
int j2 = 0;
int[] index = getIndex(chr2);获得子串的最长前缀以及最长后缀数组
while(j1<chr1.length&&j2<chr2.length)
{
if(chr1[j1]==chr2[j2]) ///若当前两个字符数组中的字符相等,则将两个字符数组的指针加1
{
j1++;
j2++;
}
else if(index[j2]==-1) ///如当前子串数值的最长前缀以及最长后缀数组的索引数组已经为-1,说明此时子串数组已经不能在往前移动,此时只能将长串向前
///移动一个位置,这就说明,当前字符与子串的第一个字符都不能进行匹配
{
j1++;
}
else ///否则即将当前子串的索引位置移动到当前字符最长前缀以及最长后缀的位置,然后继续进行比较
{
j2 = index[j2];
}
}
return j2==chr2.length ? j1 - j2:-1; 若当前子串的指针与其长度相等说明,此时已经在长串中找到符合要求的子串,
此时直接返回子串在长串中第一个字符的索引位置,否则返回-1;
}