• 沒有找到結果。

第二节、KMP 算法

在文檔中 十五个经典算法研究与总结 (頁 121-128)

这种回退就浪费了很多不必要的时间,如果能事先计算出 pattern 本身的这些性质,

那么就可以在失配时直接把 pattern 移动到下一个可能的位置,

把其中根本不可能匹配的过程省略掉,

如上表所示我们在 index=2 时失配,此时就可以直接把 pattern 移动到 index=4 的状态,

kmp 算法就是从此出发。

第二节、KMP 算法

2.1、 覆盖函数(overlay_function)

覆盖函数所表征的是 pattern 本身的性质,可以让为其表征的是 pattern 从左开始的所有连 续子串的自我覆盖程度。

比如如下的字串,abaabcaba

由于计数是从 0 始的,因此覆盖函数的值为 0 说明有 1 个匹配,对于从 0 还是从来开始计 数是偏好问题,

具体请自行调整,其中-1 表示没有覆盖,那么何为覆盖呢,下面比较数学的来看一下定义,

比如对于序列

a

0

a

1...

a

j-1

a

j

要找到一个 k,使它满足

a

0

a

1...

a

k-1

a

k=

a

j-k

a

j-k+1...

a

j-1

a

j

而没有更大的 k 满足这个条件,就是说要找到尽可能大 k,使 pattern 前 k 字符与后 k 字符相 匹配,k 要尽可能的大,

原因是如果有比较大的 k 存在,而我们选择较小的满足条件的 k,

那么当失配时,我们就会使 pattern 向右移动的位置变大,而较少的移动位置是存在匹配的,

这样我们就会把可能匹配的结果丢失。

比如下面的序列,

在红色部分失配,正确的结果是 k=1 的情况,把 pattern 右移 4 位,如果选择 k=0,右移 5 位则会产生错误。

计算这个 overlay 函数的方法可以采用递推,可以想象如果对于 pattern 的前 j 个字符,如 果覆盖函数值为 k

a

0

a

1...

a

k-1

a

k=

a

j-k

a

j-k+1...

a

j-1

a

j 则对于 pattern 的前 j+1 序列字符,则有如下可能

⑴ pattern[k+1]==pattern[j+1] 此时 overlay(j+1)=k+1=overlay(j)+1

⑵ pattern[k+1]≠pattern[j+1] 此时只能在 pattern 前 k+1 个子符组所的子串中找到相应的

overlay 函数,h=overlay(k),如果此时 pattern[h+1]==pattern[j+1],则 overlay(j+1)=h+1 否则

const int pattern_length = pattern.size();

int *overlay_function = new int[pattern_length];

int index;

overlay_function[0] = -1;

for(int i=1;i<pattern_length;++i) {

index = overlay_function[i-1];

//store previous fail position k to index;

while(index>=0 && pattern[i]!=pattern[index+1]) {

for(i=0;i<pattern_length;++i) {

cout<<overlay_function[i]<<endl;

}

delete[] overlay_function;

}

int main() {

string pattern = "abaabcaba";

compute_overlay(pattern);

return 0;

}

运行结果为:

-1 -1 0 0 1 -1 0 1 2

Press any key to continue

---

2.2、kmp 算法

有了覆盖函数,那么实现 kmp 算法就是很简单的了,我们的原则还是从左向右匹配,但 是当失配发生时,我们不用把 target_index 向回移动,target_index 前面已经匹配过的部分 在 pattern 自身就能体现出来,只要动 pattern_index 就可以了。

当发生在 j 长度失配时,只要把 pattern 向右移动 j-overlay(j)长度就可以了。

如果失配时 pattern_index==0,相当于 pattern 第一个字符就不匹配,

这时就应该把 target_index 加 1,向右移动 1 位就可以了。

ok,下图就是 KMP 算法的过程(红色即是采用 KMP 算法的执行过程):

另一作者 saturnman 发现,在上述 KMP 匹配过程图中,index=8 和 index=11

处画错了。还有,anaven 也早已发现,index=3 处也画错了。非常感谢。但图

已无法修改,见谅。

int kmp_find(const string& target,const string& pattern) {

const int target_length = target.size();

const int pattern_length = pattern.size();

int * overlay_value = new int[pattern_length];

overlay_value[0] = -1;

int index = 0;

for(int i=1;i<pattern_length;++i) {

index = overlay_value[i-1];

while(index>=0 && pattern[index+1]!=pattern[i]) {

int pattern_index = 0;

int target_index = 0;

while(pattern_index<pattern_length&&target_index<target_length) {

if(target[target_index]==pattern[pattern_index]) {

if(pattern_index==pattern_length) {

string source = " annbcdanacadsannannabnna";

string pattern = " annacanna";

cout<<kmp_find(source,pattern)<<endl;

return 0;

} (Deterministic finite state automaton DFA),DFA 可识别的文法是 3 型文法,又叫正规文法 或是正则文法,既然可以识别正则文法,那么识别确定的字串肯定不是问题(确定字串是正

在文檔中 十五个经典算法研究与总结 (頁 121-128)

相關文件