字符串的模式匹配算法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

在前面的图文中,我们讲了“串”这种数据结构,其中有求“子串在主串中的位置”(字符串的模式匹配)这样的算法。解决这类问题,通常我们的方法是枚举从A串(主串)的什么位置起开始与B串(子串)匹配,然后验证是否匹配。假设A串长度为n,B串长度为m,那么这种方法的复杂度是O(m*n)的。虽然很多时候复杂度达不到m*n(验证时只看头一两个字母就发现不匹配了),但是我们有许多“最坏情况”,比如:

A=“aaaaaaaaaaaaaaaaaaaaaaaaab”,B=“aaaaaaaab”。

大家可以忍受朴素模式匹配算法(前缀暴力匹配算法)的低效吗?也许可以,也许无所谓。

有三位前辈D.E.Knuth、J.H.Morris、V.R.Pratt发表一个模式匹配算法,最坏情况下是O(m+n),可以大大避免重复遍历的情况,我们把它称之为克努特-莫里斯-普拉特算法,简称KMP算法。

假如,A=“abababaababacb”,B=“ababacb”,我们来看看KMP是怎样工作的。我们用两个指针i和j分别表示,。也就是说,i是不断增加的,随着i 的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。

例子:

S=“abcdefgab”

T=“abcdex”

对于要匹配的子串T来说,“abcdex”首字符“a”与后面的串“bcdex”中任意一个字符都不相等。也就是说,既然“a”不与自己后面的子串中任何一字符相等,那么对于主串S来说,前5位字符分别相等,意味着子串T的首字符“a”不可能与S串的第2到第5位的字符相等。朴素算法步骤2,3,4,5的判断都是多余,下次的起始位置就是第6个字符。

例子:

S=“abcabcabc”

T=“abcabx”

如果T串后面也含有首字符“a”。对于开始的判断,前5个字符完全相等,第6个字符不等,此时,根据刚才的经验,T的首字符“a”与T的第二位字符“b”、第三位字符“c”均不等,所以不需要做判断,朴素算法步骤2,3都是多余。

因为T的首位“a”与T第四位“a”相等,第二位的“b”与第五位的“b”相等。而第四位的“a”与第五位的“b”已经与主串S中的相应位置比较过了,是相等的,因此可以断定,T的首字符“a”、第二位的字符“b”与S的第四位字符和第五位字符也不需要比较了,下次的起始位置就是6,T的起始位置就是第3个字符。

对比这两个例子,可知两种情况下主串当前位置的都是第6个字符。要考虑的变化就是子串的下标j值了。通过观察也可发现,我们屡屡提到了T串的首字符与自身后面字符的比较,发现如果有相等字符,j值的变化就会不相同。也就是说,这个j值的变化与主串其实没什么关系,关键就取决于T串的结构中是否有重复的问题。

下面按照程序的方式来介绍,字符串第一个字符的下标为0。

由于T=“abcde x”,当中没有任何重复的字符,所以j就由5变成了0。

由于T=“abcab x”,前缀的“ab”与后面“x”之前串的后缀“ab”是相等的。因此j就由5变成了2。因此,我们可以得出规律,j值的多少取决于当前字符之前的串的前,后缀的相似度。

我们把T串各个位置的j值的变化定义为一个数组next,那么next的长度就是T串的长度。于是我们可以得到下面的定义:

起始字符 next[0] = -1;

如果后面没有与起始字符相同的字符则next[i] = 0;

如果后面有与起始字符相同的字符则next[i] = -1;与前缀进行匹配,匹配完毕的后一位的j值记录匹配字符的个数。

其他next[i] = 0;

即next[i]的值表示,第i位不同时,j的取值。具体构造方法查看后面代码。

例子:

T=“abcdex”

Next = -100000

例子:

T=“abcabx”

Next = -100-102

例子:

T= “aaaaaaaab”

Next = -1-1-1-1-1-1-1-17

例子:

T =“ababacb”

Next = -10-10-130

讲完原理之后,我们利用策略模式作为解决相同问题利用不同算法的解决方案。

namespace StringMatching

{

public abstract class StringMatchingStrategy

{

public abstractint StringMatchingAlgorithm(string source, string substr);

}

}

namespace StringMatching

{

public class KmpMatching: StringMatchingStrategy {

privateint[] GetNext(string substr)

{

if(substr == null)

thrownew ArgumentException("subs tr");

int i =0, j = -1;

int[]nextVal = new int[substr.Length];

nextVal[0] = -1;

while(i < substr.Length - 1)

{

if(j == -1 || substr[i] ==

substr[j])

{

i++;

j++;

if(substr[i] != substr[j])

nextVal[i] = j;

else

nextVal[i]

=nextVal[j];

}

else

{

j = nextVal[j];

}

}

return nextVal;

}

public override int StringMatchingAlgorithm(stri ng source, string substr)

{

if(source == null)

thrownew ArgumentException("sour ce");

if(substr == null)

thrownew ArgumentException("subs tr");

int i = 0, j = 0, v;

int[]nextVal = GetNext(substr);

while(i < source.Length && j <

substr.Length)

{

相关文档
最新文档