KMP算法的特征向量
假设子串P有m个字符,子串P的特征向量N有m个非负整数,与每个字符一一对应。
也就是说每个字符都有属于自己,用来描述所在位置特征的专属数字。
那么,问题来了,这个数字的意义是什么?换句话说这个数字的作用是什么?用来描述什么?
嗯哼!敲黑板,注意听哦。
假设位置是i,N[i]是5,则5代表,从p的前五个字符顺序组成的字符串与从 i 位置开始,向左数5个字符,这5个字符从左向右组成的字符串相同。而且,没有比5大的
(⊙o⊙)…,呃,有点迷。就是 p[0]p[1]p[2]p[3]p[4] == p[i-4]p[i-3]p[i-2]p[i-1]p[i] 这个意思。。。
好了,来下一个定义吧,首先讲几个概念
//前缀子串: 模板P开头的t个字符,称为P的前缀子串,即 q[0]q[1]q[3]…q[t-1]
//i位置的左子串: 模板P的第i位置左边(包括i位置),也取出t个字符,即q[i-t+1]...q[i-2]q[i-1]q[i]
找出最长的(t最大的)能够与前缀子串匹配的i位置左子串(简称第i位的最长前缀串),t就是要求的特征数ni。
然后p中的每一个字符都有这样的一个数字,这些数字组成了p的特征向量N。
那么,重点来了,如何计算一个子串P的特征向量呢?
这里采用了递归算法,假设i-1位置的特征向量已知,借此来算i的特征向量。
递归,当i==0时,n[0]=0
i > 0 (假定知道n[i-1]==k)
如果p[i]==p[k],则n[i]==k+1;(不会比k+1大,否则n[i-1]>k)
如果p[i]!=p[k],且k!=0;则令k=n[k-1],一直循环,直到不满足。
如果p[i]!=p[k],k==0;则n[i]==0;
如果p[i]==p[k],则n[i]==k+1;
解释:这里主要是一直循环比较难理解。
当i处比较失败后,我们会缩短(如果增长的话n[i-1]>k)最长前缀子串和i-1位置的左子串继续比较,但缩短也是有规律的,我们要把这两者缩小,最长前缀子串
是从右边删减,i-1位置处的左子串是从左边删减,其余两端不变,同时要保证缩小后的两个串依然相等,我们注意到,当前最长前缀子串依然等于i-1位置的左子串
于是问题就转化成把当前最长前缀子串看成子串,找出其最右端字符的特征向量n[k-1].,确定新的当前最长前缀子串,以此类推。需要指出的是k的意义,k代表当前最长
前缀子串的再右边一位,所以这时我们依然比较p[i]与p[k],但是这时k值发生了变化,k=n[k-1].
具体的算法是
int *Next(String P)
{
int m = P.strlen();
//m为模板P的长度
assert( m > 0 );
//若m=0,退出
int *N = new int [m];
// 动态存储区开辟整数数组
assert( N != 0);
//若开辟存储区域失败,退出
N[0] = 0;
for ( int i =1 ; i < m ; i++ ) //分析P的每个位置i
{
//第(i-1)位置的最长前缀串长度
int k = N[i-1];
//以下while语句递推决定合适的前缀位置k
while( k > 0 && P[i] != P[k] ) k = N[k-1];
//根据P[i]比较第k位置前缀字符,决定N[i]
if(P[i] == P[k])
N[i] = k+1;
else N[i] = 0 ;
}
return N;
}