通俗描述
当在B中匹配A时,若在某一位失配,我们需要知道至少要将A往后移多少位才能确保之前匹配的内容仍然匹配(不然匹配肯定无效),而这个偏移量可以由“当前位置A的前缀中,A的非前缀后缀与A的前缀的最大匹配长度”计算出,考虑到在B中的计算过程实际上在计算“B的后缀与A的前缀的最大匹配长度”,两者可以用相似的做法求出,而非前缀性质可由初始化时的错位保证。
基本定义
- A:模板串,下标1~n
- B:文本串,下标1~m
- nxt[i] = max{ j | j<i 且 A[i-j+1~i] = A[1~j] }
- f[i] = max{ j | j <= i 且 j <= n 且 B[i-j+1~i] = A[1~j] }
- nxt[i]的“候选项”:{ j | j<i 且 A[i-j+1~i] = A[1~j] }
相关性质
引理1
若j是nxt[i]的候选项,则j-1必是nxt[i-1]的候选项。
引理2
若(j_0)是nxt[i]的一个候选项,则nxt[i]的小于(j_0)的最大的候选项是nxt[(j_0)]。
证:反证法,若有$ nxt[j_0] le j_1 le j_0 (,)j_1(是nxt[i]的候选项。
) ecause j_1(是nxt[i]的候选项
) herefore $ A[1~(j_1)] = A[i-(j_1)+1~i]
$ ecause j_0(是nxt[i]的候选项
) herefore $ A[1~(j_0)] = A[i-(j_0)+1~i],即A[(j_0 - j_1 +1) ~ (j_0)] = A[$ i-j_1+1$~ (i) ]
$ herefore $ A[1 ~ (j_1)] = A[(j_0-j_1+1) ~ (j_0)]
$ herefore $ nxt[(j_0)]应为(j_1)
$ herefore $ 矛盾
即得证。
计算方法
由引理1和引理2,计算nxt[i]时,只要考虑nxt[i-1]+1,nxt[nxt[i-1]]+1,...亦可理解成“A错一位后匹配它自己”
nxt求法
nxt[1]=0;
for(int i=2,j=0;i<=n;i++)
{
whiel(j>0&&a[i]!=a[j+1]) j=nxt[j];
if(a[i]==a[j+1]) j++;
nxt[i]=j;
}
f求法
for(int i=1,j=0;i<=m;i++)
{
while(j&&(j==n||b[i]!=a[j+1])) j=nxt[j];
if(b[i]==a[j+1]) j++;
f[i]=j;
if(f[i]==n) { ... }
}
nxt数组的意义
- 自身定义:nxt[i] = max{ j | j<i 且 A[i-j+1~i] = A[1~j] }
- 可将整个KMP视为一个自动机,nxt数组即为“失配边”
常见用处
- 单文本单模板字符串匹配
- 求字符串最小循环元长度(如果有,即为n-nxt[n])