Next 数组及KMP
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个getnext()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。其中最难理解的部分即next数组的求法以及回溯
前缀数组
**前缀数组中存储的是这个字符串前缀和后缀中相同子串的最长长度 **
next 数组考虑的是除当前字符外的最长相同前缀后缀
以 agctagcagctagctg 为例
** start**
将next数组初始化之后为:
–>将模串依次向后移比较至此
–>回溯
此时对比,小串长为3,j=3;回溯,j=next[j=3]=0;此时i=7;继续匹配依次后移
–>再回溯
此时对比,小串长为3,j=7;回溯,j=next[j]=3,i=14;继续匹配,当pat[i=14]=pat[j=3],i++,j++,即next[15]=4;
–>j=next[j=4]=0;–>j=next[j=0]=-1;–>i++=,j++,next[i=16]=j=0;
** end**
//获取前缀数组
void GetNext(char *pat){
int i=0,j=-1;
next[0]=-1;
while(i<n){ //n为模板串pat的长度
if(j==-1||pat[i]==pat[j]){
i++,j++;
next[i]=j;
}
else
j=next[j];
}
}
//第二种next数组
// abcab
// 00012
void calc_next() {
next[0] = 0; //下标从1开始 next[1]=0;
for (int i = 1, j = 0; i < n; i++) { //i=2;i<=n
while (j > 0 && pat[i] != pat[j])//pat[i]!=pat[j+1]
j = next[j - 1]; //j=next[j]
if (pat[i] == pat[j]) //pat[i]==pat[j+1]
j++;
next[i] = j;
}
}
献上一道前缀数组寻找循环节的问题
题号:hdu-1358
题意:一个字符串的前缀是从第一个字符开始的连续若干个字符,例如”abaab”共有5个前缀,分别是a, ab, aba, abaa, abaab。我们希望知道一个N位字符串S的前缀是否具有循环节。换言之,对于每 一个从头开始的长度为 i (i>1)的前缀,是否由重复出现的子串A组成,即 AAA…A (A重复出 现K次,K>1)。如果存在,请找出最短的循环节对应的K值(也就是这个前缀串的所有可能重复节 中,最大的K值)。
输入:输入包括多组测试数据,每组测试数据包括两行。
第一行输入字符串S的长度N。
第二行输入字符串S。
输入数据以只包括一个0的行作为结尾。
输出:对于每组测试数据,第一行输出 “Test case #” 和测试数据的编号。
接下来的每一行,输出具有循环节的前缀的长度i和其对应K,中间用一个空格隔开。
前缀长度需要升序排列。
在每组测试数据的最后输出一个空行。
#include<stdio.h>
#include<string.h>
const int maxn = 1000000 + 5;
char pat[maxn];
int next[maxn];
int len;
void getnext() {
int i = 0, j = -1;
next[0] = -1;
while (i < len) {
if (j == -1 || pat[i] == pat[j]) {
i++, j++;
next[i] = j;
}
else
j = next[j];
}
}
int main()
{
int t = 0;
while (scanf("%d", &len) != EOF && len) {
memset(pat, '\0', sizeof(pat));
scanf("%s", pat);
getnext();
printf("Test case #%d\n", ++t);
for (int i = 2; i <= len; i++) {
if (!(i % (i - next[i])) && i / (i - next[i]) != 1)
printf("%d %d\n", i, i / (i - next[i]));
}
printf("\n");
}
return 0;
}
KMP 匹配
理解了怎么获得前缀数组,那么我们来浅谈KMP的匹配模式:
假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置
如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
如果j != -1, 且当前字符串匹配失败(即s[i] != p[i]),则令i不变,j = next[j] 。此时意味着匹配失败,模板串pat相对于主串str向右移 j-next[j]。
void KMP()
{
GetNext(pat);
int i = 0, j = 0;
int sum = 0;
while (i < m) //m为主串str长度
{
if (j == -1 || str[i] == pat[j])
i++, j++;
else
j = Next[j];
if (j == n) //当前模板串pat与主串str其中一部分匹配成功
//根据题意添加条件
}
return ;
}