您的位置:首页 > 其它

模式匹配算法BF和KMP

2016-11-24 20:17 204 查看

BF(Brute-Force)算法

步骤:

1:利用计数指针i和j指示主串S和模式T中当前正在比较的字符的位置,i的初始值pos可为任意小于主串S长度的值,j初值为1;
2:若两串均未比较到串尾
a:若S[i]=T[j],则i和j分别指示串中的下一个位置,继续比较后面的字符;
b:若S[i]不等于T[j],则指针后退重新比对,从主串的下一个字符(i=i-j+2)起重新开始和模式T的第一个字符比较
3:若j>T.length,说明匹配成功,返回和模式T中第一个匹配相等的字符在主串S中的序号(i-T.length);否则,返回0

注意:数组下标从1开始计数,所以i=i-j+2;

算法描述(c++):

int BF(string S,string T,int pos)
{
int i,j;
i=pos;j=1;
while(i<=S.length&&j<=T.length){
if(S[i]==T[j]){
i++;
j++;
}
else{
j=1;
i=i-j+2;
}
}
if(T.length<j)
return i-T.length;
return 0;
}

匹配过程:

S:aaaaaba
T:aab i=pos=1,j=1 i=2;j=1 i=3,j=1;
第一次匹配:a a a a a b a 第二次匹配:a a a a a b a 第三次匹配:a a a a a b a

a a b a a b a a b
i=3,j=3;                                           i=4,j=3                                          i=5,j=3;

i=4,j=1
第四次匹配: a a a a a b a
                              a a b
   i=7,j=4
返回i-T.length=7-3=4

KMP算法(改进的BF算法)

例1:
S:abcdefab
T:abcdex

第一次匹配:a b c d e f a b        第二次匹配:a b c d e f a b    第三次匹配:a b c d e f a b
                     a b c d e x                                         a                                                    a
i=6,j=6
i=2,j=1; i=3,j=1;

第四次匹配:a b c d e f a b        第五次匹配:a b c d e f a b    第六次匹配:a b c d e f a b 
                              a                                                     a                                                a
i=4,j=1                             i=5,j=1;                                    i=6,j=1
通过观察我们可以看出,对于匹配的子串T来说,“abcdex”首字母‘a’与后面的串“bcdex”中任意一个字符都不相等,对于第一步而言,前五位字符分别相等,这就意味着T的首字符不可能与S串中的第二位到第五位的字符相等,所以第二步到第五步都是多余的。

例2:
S:abcababca...
T:abcabx

第一次匹配:a b c a b a b c a...   第二次匹配:a b c a b a b c a...   第三次匹配:a b c a b a b c a...
      a b c a b x                                       a                                                       a 
i=6,j=6                                     i=2,j=1                                                i=3,j=1

第四次匹配:a b c a b a b c a...    第五次匹配:a b c a b a b c a...    第六次匹配:a
b c a b a b c a ...
      a                                                    a  b                                                 a b c 
                 
4000
                    i=4,j=1                                               i=5,j=2                                              i=6,j=3

通过观察我们可以发现,第二步和第三步是多余的。
T的首位‘a’和T的第四位‘a’相等,第二位‘b’和第五位‘b’相等,而在第一步,第四位的‘a’和第五位的‘b’已和主串S中的相应位置比较过,是相等的,因此可以判断,T的第一位和第二位的字符与S的第四位和第五位也不用比较了,所以第四次和第五次也是多余的。

在BP算法中,主串的i值需要不断地回溯,而通过上面的例子可以看出i回溯可以不需要,KMP算法就是避免不必要的回溯发生。
既然i值不能回溯,那就考虑j值得变化,通过观察可以发现例1中T=“abcdex”,当中没有任何重复的字符,所以j由6变成1,而例2中T=“abcabx”,前缀的“ab”与最后“x”之前串的后缀“ab”相等,所以j由6变成3,因此我们可以得出规律,j值取决于当前字符之间的串的前后缀的相似度。

把T串中各个位置的j值得变化定义为一个数组next,next的长度就是T的长度,next[1]=0

next数组的推导

T="abcdex"

         j:1 2 3 4 5 6   
       T: a b c d e x
next[j]: 0 1 1 1 1 1
1)当j=1时,next[1]=0;
2)当j=2时,j由1到j-1就只有字符‘a’,next[2]=1;
3)当j=3时,j由1到j-1的字符串“ab”,显然“a”不等于“b”,next[3]=1;
4)同理,所以T串的next[j为011111

T=“abcabx”

      j:1 2 3 4 5 6 
        T:a b c d e x
next[j]: 0 1 1 1 2 3
1)当j=1时,next[1]=0;
2)当j=2时,j由1到j-1就只有字符‘a’,next[2]=1;

3)当j=3时,j由1到j-1的字符串“ab”,显然“a”不等于“b”,next[3]=1;

4)当j=4时,j由1到j-1的字符串“abc”,显然不相等,next[4]=1;
5)当j=5时,j由1到j-1的字符串“abca”,前缀字符a与后缀字符“a”相等,因此可推算出next[5]=2;
6)当j=6时,j由1到j-1的字符串“abcab”,前缀“ab”与后缀“ab”相等,next[6]=3;

算法实现

void get_next(string T,int *next)

{

    int i,j;

    i=0;

    j=1;

    next[1]=0;

    while(j<T.length())

    {

        if(i==0||T[j]==T[i])

        {

            ++i;

            ++j;

            next[j]=i;

        }

        else

            i=next[i];

    }

}

int KMP(string S,string T,int pos)

{

    int i=pos;

    int j=1;

    int next[255];

    get_next(T,next);

    while(i<=S.length()&&j<=T.length())

    {

        if(j==0||S[i]==T[i])

        {

            ++i;

            ++j;

        }

        else

            j=next[j];

    }

    if(j>T.length())

        return i-T.length();

    return 0;

}

KMP算法改进

例:
S=“aaaabcd”
T=“aaaaax”

1:a a a a b c d
2: a a a a b c d     3:a a a a b c d         4:a a a a b c d       5:a a a a b c d         6:a a a a b c d
 |                              |                           |                              |                             |                                  |
     a a a a a x                 a a a a a x
      a a a a a x                  a a a a a x                 a a a a a x                  a
i=5,j=5                   i=5,j=4                      i=5,j=3                    i=5,j=2                 i=5,j=1                     i=6,j=1

如上面的例子,next数组值是012345,通过观察发现,2345步骤是多余的,由于第二,三,四,五位置的字符斗鱼首位‘a相等,可以用首位的next【1】的值去取代后续的next【j】的值,因此增加一个取代的数组nextval

nextval数组推导

T=“ababaaaba”
     j:1 2 3 4 5 6 7 8 9 
   T:a b a b a a a b a
     next[j]:0 1 1 2 3 4 2 2 3
nextval[j]:0 1 0 1 0 4 2 1 0

1)当j=1时,nextval[1]=0;
2)当j=2时,第二个字符‘b’的next值是1,第一位是‘a’,不相等,所以nextval[2]=next[2]=1,维持原值
3)当j=3时,第三个字符‘a’的next值是1,第一位是‘a’,相等,所以nextval[3]=nextval[1]=0;
以此类推

算法实现

void get_nextval(string T,int *next)
{

    int i,j;

    i=0;

    j=1;

    nextval[1]=0;

    while(j<T.length())

    {

        if(i==0||T[j]==T[i])

        {

            ++i;

            ++j;

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

                nextval[j]=i;

            else

                nextval[j]=nextval[i];

        }

        else

            i=nextval[i];

    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: