[基于前缀搜索的算法] shift-and/shift-or算法

      与KMP算法一样,shift-and 和shift-or算法都是基于前缀搜索的字符串匹配算法。但是该算法的思想比KMP简单很多,它维护一个字符串的集合,集合中的每个字符串既是模式串P的前缀,同时也是已读入文本的后缀。每读入一个新的文本字符,该算法即用位并行的方法更新集合。该集合用一个位掩码D=dm......d1来表示。

       先说shift-and算法。

       D的第j位被置为1(称为D的第j位是活动的)当且仅当p1...pj是t1....tj的后缀。当dm是活动时,就表示有一个成功的匹配。

       当读入下一个字符ti+1时,需要重新计算新的位掩码D’。D’的第j+1位是活动的当且仅当D的第j位是活动的(即p1.....pj是t1.....tj的后缀)并且ti+1与pi+1相等。利用位并行,D’的计算很容易在常数时间内完成。

       shift-and算法首先构造一个表B,记录字母表中每个字符的位掩码bm.....b1.如果pj = c,掩码B[c] 的第j位被置为1,否则0。

       首先置D = 0m (就是m个0的意思),对于每个新读入的文本字符ti+1,可以用如下公式对D进行更新:

                                  D’ = (( D<<1 | 0m-11 ) & B[ti+1] ) ;

      

       直观上来讲,左移位操作<<讲D‘的第i+1位的值置为D的第i位。因为空字符串ε也是文本的后缀,所以D<<1需要在最低位与0m-11进行位或操作.因为要找到那些满足ti+1=pi+1的位置,所以需要再将上面的结果与B[ti+1]进行位与操作.

       当模式串的长度不超过几个机器字长时,shift-and算法的时间复杂度为O(n)。

       shift-and算法用到了位运算。

       关于shift-and算法我现在还是没能很彻底的想透它的原理,不知道为什么这样就能完成字符串匹配的搜索了。但是我现在知道怎么用它,也知道它是怎么操作的...

       下面是我的 shift-and算法C++版实现代码:

 1 void shift_and(char *Pstr, char *Tstr)
 2 {
 3     int i,Pstr_len,Tstr_len,D;
 4     Pstr_len = strlen(Pstr);
 5     Tstr_len = strlen(Tstr);
 6     unsigned int B[128];//128位假设是
 7 
 8     //Preprocessing
 9     for(i=0;i<128;i++)
10     {
11         B[i]=0;
12     }
13     for(i=0;i<Pstr_len;i++)
14     {
15         B[Pstr[i]] = B[Pstr[i]] | ( 1<<i);
16     }
17 
18     //Searching
19     D = 0;
20     for(i=0;i<Tstr_len;i++)
21     {
22         D = ((D<<1)|1) & B[Tstr[i]] ;
23         if( D &(1<<(Pstr_len-1)))
24         {
25             cout<<i-Pstr_len+1<<endl;
26         }
27     }
28 
29 }

        而shift_or算法是shift_and的一种更富有技巧性的实现.其主要思想是通过位取反以去掉公式中的掩码0m-11,从而加速D'的计算.该算法使用反码表示B中的位掩码和位向量D,因为左移位操作"<<"会自动在D'右端引入0,于是空字符串就被纳入D'中. 同时,B和D也要做出相应的更改.

        下面是我的shift-or算法c++版的实现:

 1 void shift_or(char *Pstr, char *Tstr)
 2 {
 3     int i,Pstr_len,Tstr_len,D, mask;
 4     Pstr_len = strlen(Pstr);
 5     Tstr_len = strlen(Tstr);
 6     unsigned int B[128];//128位假设是
 7 
 8     //Preprocessing
 9     for(i=0;i<128;i++)
10     {
11         B[i]=~0;         //设为全部非0
12     }
13     for(i=0;i<Pstr_len;i++)
14     {
15         B[Pstr[i]] = B[Pstr[i]] & (~( 1<<i));     //其实就是shift-and反过来
16     }
17     mask= ~(1<<Pstr_len-1);
18 
19     //Searching
20     D = ~0;                   //反的
21     for(i=0;i<Tstr_len;i++)
22     {
23         D = (D<<1) | B[Tstr[i]] ;             //少了一次位运算
24 
25         if(~(D|mask))
26         {
27             cout<<i-Pstr_len+1<<endl;
28         }
29     }
30 
31 }

    根据实验,shift-or算法只适用于字母表较小并且模式串长度较短的情况.

posted on 2012-07-04 16:05  unhealthy  阅读(1572)  评论(0编辑  收藏  举报