字符串匹配算法

字符串匹配算法有好多,朴素的,KMP,有限自动机,BM算法以及sunday算法,貌似还有个KR算法?

我只知道朴素的,KMP(刚刚学的),sunday算法(改进的BM算法)。。。

朴素字符串匹配算法:

 1 int naive_match(char *str, char *patt)
 2 {
 3     if(str==NULL || patt==NULL)
 4         return -1;
 5     int i, j,
 6     int str_len = strlen(str);
 7     int patt_len = strlen(patt);
 8     
 9     for(i=0; i<(str_len-patt_len); i++)
10     {
11         j = 0;
12         while(j<patt_len && patt[j]==str[i+j])
13             j = j+1;
14         if(j==patt_len)
15             return i;
16     }
17     return -1;
18 }

KMP算法

这个算法。。。我只能说我太笨(智商永远是硬伤啊)。。。理解了好长时间,也好几次。。。

(推荐一个博客,这个人很厉害。。。http://www.matrix67.com/blog/,里面有一篇讲解KMP算法的。。。)

不好意思,matrix67,我就直接复制粘贴了

 

假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。

    i = 1 2 3 4 5 6 7 8 9 ……
    A = a b a b a b a a b a b …
    B = a b a b a c b
    j = 1 2 3 4 5 6 7

    此时,A[6]<>B[6]。这表明,此时j不能等于5了,我们要把j改成比它小的值j'。j'可能是多少呢?仔细想一下,我们发现,j'必须要使得B[1..j]中的头j'个字母和末j'个字母完全相等(这样j变成了j'后才能继续保持i和j的性质)。这个j'当然要越大越好。在这里,B [1..5]="ababa",头3个字母和末3个字母都是"aba"。而当新的j为3时,A[6]恰好和B[4]相等。于是,i变成了6,而j则变成了 4:

    i = 1 2 3 4 5 6 7 8 9 ……
    A = a b a b a b a a b a b …
    B =     a b a b a c b
    j =     1 2 3 4 5 6 7

    从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。
    再后来,A[7]=B[5],i和j又各增加1。这时,又出现了A[i+1]<>B[j+1]的情况:

    i = 1 2 3 4 5 6 7 8 9 ……
    A = a b a b a b a a b a b …
    B =     a b a b a c b
    j =     1 2 3 4 5 6 7

    由于P[5]=3,因此新的j=3:

    i = 1 2 3 4 5 6 7 8 9 ……
    A = a b a b a b a a b a b …
    B =         a b a b a c b
    j =         1 2 3 4 5 6 7

    这时,新的j=3仍然不能满足A[i+1]=B[j+1],此时我们再次减小j值,将j再次更新为P[3]:

    i = 1 2 3 4 5 6 7 8 9 ……
    A = a b a b a b a a b a b …
    B =             a b a b a c b
    j =             1 2 3 4 5 6 7

    现在,i还是7,j已经变成1了。而此时A[8]居然仍然不等于B[j+1]。这样,j必须减小到P[1],即0:

    i = 1 2 3 4 5 6 7 8 9 ……
    A = a b a b a b a a b a b …
    B =               a b a b a c b
    j =             0 1 2 3 4 5 6 7

    终于,A[8]=B[1],i变为8,j为1。事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。

复制黏贴完毕!(我这么笨的都理解了(虽然代码不知道怎么写(代码能力很差的说!!!)),相信大家都能理解的!)

其实,KMP的预处理本身是一个模式串首尾“自我匹配”的过程!

代码:

get_next()

 1 void get_next(char *patt, int next[])
 2 {
 3     int i=0;
 4     int j=-1;
 5     if(patt[i])
 6        next[i] = j;
 7    else
 8        return;
 9    while(i<strlen(patt))
10    {
11        if(j==-1 || patt[i]==patt[j])
12        {
13            ++i;
14            ++j;
15            if(patt[i]!=patt[j])
16            {
17                next[i] = next[j];
18            }
19            else
20                next[i] = j; 
21     }
22       else
23          j = next[j];
24   }
25 }

kmp()

 1 int kmp(char *str, char *patt)
 2 {
 3     int i,j;
 4     i = 0;
 5     j = 0;
 6     int str_len = strlen(str);
 7     int patt_len = strlen(patt);
 8     int *next = new int[patt_len+1];
 9     get_next(patt, next);
10     while(i<str_len && j<patt_len)
11     {
12         if(j==-1 || str[i] == patt[j])
13         {
14             ++i;
15             ++j;
16         }
17          else
18              j = next[j];
19   }
20     if(j==patt_len)
21         return i-patt_len;
22     else
23         return -1;
24 }

Sunday算法(思想很。。。)

直接上代码吧?还是先复制一段从ACM代码库(貌似吉林大学的)中复制过来的分析吧。。。

例如我们要在"substring searching algorithm"查找"search",刚开始时,把子串与文本左边对齐: 
substring searching algorithm
search
结果在第二个字符处发现不匹配,于是要把子串往后移动。但是该移动多少呢?这就是各种算法各显神通的地方了,最简单的做法是移动一个字符位置;KMP是利用已经匹配部分的信息来移动;BM算法是做反向比较,并根据已经匹配的部分来确定移动量。这里要介绍的方法是看紧跟在当前子串之后的那个字符(第一个字符串中的'i')。 显然,不管移动多少,这个字符是肯定要参加下一步的比较的,也就是说,如果下一步匹配到了,这个字符必须在子串内。所以,可以移动子串,使子串中的最右边的这个字符与它对齐。现在子串'search'中并不存在'i',则说明可以直接跳过一大片,从'i'之后的那个字符开始作下一步的比较,如下: 
substring searching algorithm 
          search 
比较的结果,第一个字符就不匹配,再看子串后面的那个字符,是'r',它在子串中出现在倒数第三位,于是把子串向后移动三位,使两个'r'对齐,如下: substring searching algorithm 
                search
这次匹配成功了!

代码:

 1 int sunday(char *str, char *patt)
 2 {
 3     int temp[256];
 4     int *shift = temp;
 5     int str_len = strlen(str);
 6     int patt_len = strlen(patt);
 7 
 8     int i;
 9     for(i=0; i<256; i++)
10         *(shift+i) = patt_len+1;
11     for(i=0; i<patt_len; i++)
12         *(shift+char(*(patt+i))) = patt_len - i;
13     int limit = str_len - patt_len +1;
14     for(i=0; i<limit; i+=shift[str[patt_len+i]])
15     {
16         if(str[i]==*patt)
17         {
18             char *match_text = str+i+1;
19             int match_size = 1;
20             do{
21                 if(match_size==patt_size)
22                     return i;
23             } while((*match_text++)==patt[match_size++]);
24         }
25     }
26   return -1;
27 }

 

 

 

posted @ 2012-10-08 22:11  iavailable  阅读(252)  评论(0编辑  收藏  举报