KMP
作用:字符串匹配
例:给定一个$m$位字符串$s$,一个$n$位模式串$p$,求出$p$在$s$中所有出现的位置的起始下标
分析:
暴力算法:
1 | char s[N],p[N]; |
考虑优化:
使用$ne[j]$记录$p$中$[1,j]$的前缀和后缀的最大相同长度,即$p$中$p[1,ne[j]]=p[j-ne[j]+1,j]$
$ne[j]$用于求若匹配失败则最少往后移动多少可以成功匹配。
用$j$表示模式串$p$已经有几位匹配成功,若$p[j]{=}\mathllap{/,}s[i+1]$,则表示当前位不匹配,此时把$p$向后移动$ne[j]$位即可保证$p[1\thicksim j-1]$再次匹配。

如上图,若在绿线之前均匹配,绿线之后不匹配,则最少把$p$向后移动$ne[j]$位至第二条线,此时三条黑线均相等。
由此,可以想到KMP的匹配:
- 若$j$没有退回起点且当前的$s[i]{=}\mathllap{/,}p[j+1]$,则把模式串向后移$ne[j]$位,此时的$j=ne[j]$,重复直至$s[i]=p[j+1]$
- 此时因为$s[i]=p[j+1]$,所以$p$中匹配的个数由$j$变成$j+1$,所以$j++$
- 若$j=n$,即模式串$p$完全匹配,则输出答案继续下一轮匹配($j=ne[j]$)
1 | for(int i=1,j=0;i<=m;i++){ |
$ne$数组求法:例:$p=$”$abcab$”
| $p$ | $a$ | $b$ | $c$ | $a$ | $b$ |
|---|---|---|---|---|---|
| 下标 | $1$ | $2$ | $3$ | $4$ | $5$ |
| $ne[]$ | $0$ | $0$ | $0$ | $1$ | $2$ |
$ne[1]$:前缀=$\phi$,后缀=$\phi$,$ne[1]=0$
$ne[2]$:前缀=${a}$,后缀=${b}$,$ne[2]=0$
$ne[3]$:前缀=${a,ab}$,后缀=${c,bc}$,$ne[3]=0$
$ne[4]$:前缀=${a,ab,abc}$,后缀=${a,ca,bca}$,$ne[4]=1$
$ne[5]$:前缀=${a,ab,abc,abca}$,后缀=${b,ab,cab,bcab}$,$ne[5]=2$
具体方法:类似KMP的匹配

当$p[1\thicksim j]=p[j-ne[i-1]+1,j]$,且$p[i]{=}\mathllap{/,}p[j+1]$时,模式串$p$一直向后移($j=ne[j]$)直至$p[i]=p[j+1]$,此时表示$[1,i]$的前后缀的相同最大长度为$j+1$,即$ne[i]=j+1$
步骤:
- 若$j$没有退回起点且当前的$s[i]{=}\mathllap{/,}p[j+1]$,则把模式串向后移$ne[j]$位,此时的$j=ne[j]$,重复直至$s[i]=p[j+1]$
- 此时因为$s[i]=p[j+1]$,所以$p$中匹配的个数由$j$变成$j+1$,所以$j++$
- 则$ne[i]=j$
1 | for(int i=2,j=0;i<=n;i++){ |
总体代码:
1 |
|