赞
踩
模式匹配即子串定位操作Index(S,T,pos),是串处理系统中最常见的操作(如查找),从主串S第pos个位置开始查找与模式串T相等的子串
例子:主串S=“ababcabaabcac”,模式串T=“abaabcac”,从S的第1个位置开始查找与T相等的子串。
说明:本文所用数组的0号位用于存储串长度,故如:串S的第i位可用S[i]表示。
算法思想:从主串第pos个字符起(i),//逐个字符与T中当前字符(j)比较,若相同则比较下一个(i++,j++),不同则i回溯到S中“下一个开始比较的地方”,重新比较//如此重复直到匹配成功(j>T[0])或者i越界(i>S[0])
说明:在第i个时失配如何将i后退到“下一个开始比较的地方”?
S到到目前已匹配了j-1个,故第一个匹配的位序是i-(j-1),故“下一个开始匹配的是i-j+2
通过这个例子我们发现,BF算法实际每次失配都将回溯从下个开始位置再比较,能否不回溯从而减少不必要的比较次数呢?
kmp算法就找到了方法,解决了回溯的问题。
我们先看上面蛮力算法求解例子的过程。通过第一次的查找,我们其实已经知道主串S的前面3位是什么了。要想在第二次查找时不回溯,那我们就需要考虑一个问题:我们应该让模式串T的哪个位与主串S当前位继续进行比较。这个问题我们要怎么求解呢?
假设此次查找比较到S[i]与T[j]不匹配了,然后选T[k]继续与S[i]进行比较。那就隐含着T的1到k-1位是与S的i-k+1到i-1位刚好匹配。
即:找到S中截止到S[i-1]的真右子串,它与T中T[1]开始的左子串相等且该子串尽量长,S[i]接下来应该比较的字符为T[子串长度+1]
i不回溯,当S[i]与T[j]失配时设法找到下一个与S[i]进行比较的T[k]即可。关键在于求k,实际k只与j相关,如何求k=next[j]?
下面给出next[j]的人工求解过程:
j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
T | a | b | a | a | b | c | a | c |
next[j] | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
算法思想:从主串第pos个字符起(i),逐个字符与T中当前字符(j)比较,若相同则比较下一个(i++,j++),不同则令j=next[j],重新比较S[i]与T[j],如此重复直到匹配成功(j>T[0])或者i越界(i>S[0])
int Index_KMP(SString S, SString T, int pos) {
int i = pos;
int j = 1;
while (i <= S[0] && j <= T[0]) {
if (S[i] == T[j] || j == 0 ) {
++i;
++j;
} //匹配或next==0则后移
else {
j=next[j];
} //特点:S的i指针不回溯,S[i]重新与T[j]比较
}
if (j > T[0]) return i-T[0]; //子串结束,说明匹配成功
else return 0;
} //Index_KMP
主串为S=“aaabaaaab”,模式串T=“aaaab”。
按照kmp算法,先求next[j]如下:
j | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
T | a | a | a | a | b |
next[j] | 0 | 1 | 2 | 3 | 4 |
查找过程如下:
针对引例求解如下:
j | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
T | a | a | a | a | b |
next[j] | 0 | 1 | 2 | 3 | 4 |
nextval[j] | 0 | 0 | 0 | 0 | 4 |
… | T [ i ’ − j + 1 ] T[i’-j+1] T[i’−j+1] | T [ i ’ − j + 2 ] T[i’-j+2] T[i’−j+2] | … | T [ i ’ − 1 ] T[i’-1] T[i’−1] | T [ i ’ ] T[i’] T[i’] | T [ i ] T[i] T[i] |
---|---|---|---|---|---|---|
T [ 1 ] T[1] T[1] | T [ 2 ] T[2] T[2] | … | T [ j − 1 ] T[j-1] T[j−1] | T [ j ] T[j] T[j] | … |
已
知
:
T
[
1
]
=
0
已知:T[1] = 0
已知:T[1]=0;
记
i
’
=
i
−
1
,
若
n
e
x
t
[
i
’
]
=
j
,
T
[
1..
j
−
1
]
=
=
T
[
i
’
−
j
+
1..
i
’
−
1
]
,
下
面
求
n
e
x
t
[
i
]
记i’=i-1,若next[i’]=j,T[1..j-1]==T[i’-j+1..i’-1],下面求next[i]
记i’=i−1,若next[i’]=j,T[1..j−1]==T[i’−j+1..i’−1],下面求next[i]
void get_next(SString &T, int &next[]) { // 求模式串T的next函数值并存入数组next int i = 1, j = 0; next[1] = 0; while (i < T[0]) {//共有T[0]个next值需要求取 if (j == 0 || T[i] == T[j]) { //第一种情况或者第二种情况下j=0 ++i; ++j; next[i] = j; } else j = next[j];//第二种情况下j变成next[j] } } // get_next 复杂度:O(T[0]) void get_nextval(SString &T, int &next[]) { // 求模式串T的nextval函数值并存入数组nextval int i = 1, j = 0; nextval[1] = 0; while (i < T[0]) {//共有T[0]个next值需要求取 if (j == 0 || T[i] == T[j]) {//第一种情况或者第二种情况下j=0 ++i; ++j; if (T[i] != T[j]) nextval[i] = j; else nextval[i] = nextval[j]; } else j = nextval[j];//第二种情况下j编程next[j] } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。