今天看了不少关于KMP算法的东东 读了好几篇别人写的博客 零零碎碎的算是大体明白是个怎么回事儿了
现在好好把别人的东西 整理整理 部分是转载的哦!
KMP算法核心:
1、KMP算法借助于一个辅助数组next来确定当匹配过程中出现不等时,模式P右移的位置和开始比较的位置。
2、next[i]的取值只与模式P本身的前i+1项有关,而与目标T无关。
3、匹配过程中遇到Pi不等于Tj时,若next[i]>=0,则应将P右移i-next[i]位个字符,用P中的第next[i]个字符与Tj 进行比较
(这句话也可以这样理解 当出现不匹配时(不匹配位置为i) 计算 i=next[i], 将模式P i处的值与不匹配的原窜对齐,继续比较)
4、若:next[i]= -1,P中的任何字符都不必再与Tj比较,而应将P右移i+1个字符,从P0和Tj+1从新开始下一轮比较
next的计算方法如下(转的,我觉得讲的挺好 而且考虑的挺全面的)
书上有的东西我就不说了,那些东西网上一搜一大片,我主要说一下我理解的由前缀函数生成的next数组的含义,先贴出求next数组的方法。
void GetNext(char* t, int* next)
{
int i, j, len;
i = 0;
j = -1;
next[0] = -1;
while(t[i] != '\0')
{
if (j == -1 || t[i] == t[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}
例如:
若 i = 4 ,则 i - 1 = 3 , m = next[4] = 2
从T[0...3]截取长度为2的子串,为"ab"
从T[0..3]截取最后2个字符,为"ab"
此时2个子串相等,则说明 next[4] = 2 成立,也可证明 m = 2 为最大的m值。
本来一开始我是没有加"不为自身"这个限制条件的,可是后来我发现一种情况:
若 i = 4 ,则 i - 1 = 3 , m = next[4] = 3
从T[0...3]截取长度为3的子串,为"aaa"
从T[0..3]截取最后3个字符,为"aaa"
此时2个子串相等,则说明 next[4] = 3 成立。
但是我发现如果next[4] = 4: //这里容易犯错!!!
从T[0...3]截取长度为4的子串,为"aaaa"
从T[0..3]截取最后4个字符,为"aaaa"
此时2个子串也是相等的,那么是不是说明 next[4] 应该等于4呢?
仔细观察后发现,如果 next[4] = 4 ,那么T[0...3]的前4个字符和后4个字符是重合的,并且重复子串和T[0...3]也是相等的。看过教材后发现教材中给出的前缀函数定义有一句为:next[j] = max{k | 0 < k < j 且 'p[0]...p[k-1]' = 'p[j-k+1]...p[j-1]'},应该不包含子串为本身的情况...
这样再做PKU 2406 和 PKU 1961 的时候就很简单了,用 length - next[length] 求出"不为自身的最大首尾重复子串长度",此时需要多求一位next[length]值,若最大重复子串的长度是length的非1整数倍,则证明字符串具有周期重复性质。
PKU 2752 是求 前缀 == 后缀 的长度,也就是首尾重复子串长度,利用next数组记录的"不为自身的最大首尾重复子串长度"可以马上得到结果。
举例说明:
目标串:T: abababcb
模式串: P:ababc
计算P的next 函数
i 0 1 2 3 4
s a b a b c
next[i] -1 0 0 1 2
开始匹配
第一步: 不匹配的为c index为4 next[4]=2
T a b a b a b c b
P a b a b c
第二步:将index为next[4]=2的数字(绿色标示)与 T 中标红的a对齐
T a b a b a b c b
P a b a b c
这里的例子举得有点简单 一下子就对其了 复杂的道理一样!代码如下所示:
int kmp(char T[],char P[],int next[])
{
int i=0,j=0,len1=strlen(T),len2=strlen(P);
while((i<len1)&&(j<len2))
{
if(j==-1||T[i]==P[j])
{
j++;
i++;
}
else
j=next[j];
}
if(j==len2) return i-len2;
else return -1;
}