WM算法原理与代码实现(模式匹配)

 WM算法采用字符块技术,增大了主串和模式串不匹配的可能性.从而增加了直接跳跃的机会:它使用前缀表进一步过滤不匹配的模式串,使算法获得了较高的运行效率。因此.在现有的多关键字匹配算法中,使用块字符、Hash技术和前缀特征表技术的WM算法通常被认为具有最高的效率。
   Wu-Manber 算法采用了跳跃不可能匹配的字符策略和hash 散列的方法,加速匹配的进行.该方法需要对所有模式进行预处理,构建SHIFT,HASH 和PREFIX 这3 个表,便于后续处理。SHIFT 表用于在扫描文本串的时候,根据读入字符串决定可以跳过的字符数,如果相应的跳跃值为0,则说明可能产生匹配,就要用到HASH 表和PREFIX 表进一步判断,以决定有哪些匹配候选模式,并验证究竟是哪个或者哪些候选模式完全匹配.下面首先介绍预处理过程:
   假设模式集合P 中最短的模式长度为m,那么,后续讨论仅仅考虑所有模式的前m 个字符组成的模式串,即要求所有匹配的模式长度相等.为了加快比较速度,对长为m 的串进行分组,以B 个长度的字符串为基本单位,每次比较长度为B 的子串.对于B 的选取,原文给出了指导公式计算出一个合适的B 值:B=logc2M.这里,M=k×m,k 是模式的数目;而c 表示字符集的大小即c=|Σ|.
设X=x1…xB 为T 中的待比较的长度为B 的子串,通过hash 函数映射得到一个索引值index,以该索引值作为偏移得到SHIFT 表中的值,该值决定读到当前子串X 后可以跳过的位数.假设X 映射到SHIFT 表的入口为index 的表项,即index=hash(X):
  SHIFT[index]=m-B+1  x在模式串中没有出现
              =min{m-j|X[k]=pi[j-B+k],1<=k<=m} x在模式串中出现
这样,就需要将每个模式中长度为B 的子串aj−B+1…aj 映射到SHIFT 表中去,这里的映射函数是和上面相同的hash 函数。
设当前比较的文本字串X 的hash 值为h,如果SHIFT[h]=0,说明可能产生了匹配,那么需要进一步进行判断。于是,用该h 值作为索引,查HASH 表找到HASH[h],它存储的是指针,指向两个单独的表:一个是模式链表,另一个是PREFIX 表.模式链表中存放的是后B 个字符的hash 值同为h 的所有模式.PREFIX 表存储的是模式链表中每个模式的前缀hash 值,它有利于进一步减少实际比较次数,因为往往有不少模式具有相同后缀,也就有相同的hash 值,这样,它们都指向HASH 表的同一表项,从而需要逐一比较每个可能的模式,增加了比较负担,通过引入前缀hash 值的比较能够加速处理.对于待比较长度为m 的串,如果其长度为B′的前缀与模式的前缀的hash值也相同,则再将相应的文本串与符合的模式逐一进行比较,最终判定是否完全匹配.
预处理过程结束,完成3 个表的构建.
   下面讨论扫描文本进行比较匹配的过程.匹配从文本的第m 个字符开始,文本的扫描从左向右;对模式的匹配是从模式的后面向前进行的,即从右向左.每次扫描B 个字符tm−B+1…tm,按如下步骤进行:

Step1 现正扫描的text的末B位tm-B+1…tm通过hash function计算其哈希值h。

Step2 查表,SHIFT[h]>0,text小指针后滑SHIFT[h]位,执行1;SHIFT[h]=0,执行3;

Step3 计算此m位text的前缀的哈希值,记为text_prefix;

Step4 对于每个p(HASH[h]≦p)看是否PREFIX[p]=text_prefix。

    如果相等,方才让真正的pat(即PAT_POINT[p])去和text匹配。

Wu-Manber 算法的时间复杂度平均情况是O(BN/m)。B 是块字符的长度,而N 是文本的长度,m 是模式的最短长度。该算法对最短模式长度敏感,SHIFT 函数的最大值受最短模式长度的限制,如果最短模式长度很短,则移位的值不可能很大,因此对匹配过程的加速有限。
上述的理论知识比较枯燥,下面来看一下算法的C具体实现过程:
WM.h文件如下:
#ifndef WM_H
#define WM_H
#include 
#include 
#include
#define HASHTABLESIZE (256*256)
#define MAXLEN 256
typedef struct wm_pattern_struct
{
 struct wm_pattern_struct *next;
 unsigned char *psPat; //pattern array
 unsigned psLen;    //length of pattern in bytes
}WM_PATTERN_STRUCT;
#define HASH_TYPE short
#define SHIFTTABLESIZE (256*256)
typedef struct wm_struct
{
 WM_PATTERN_STRUCT *plist; //pattern list
 WM_PATTERN_STRUCT *msPatArray; //array of patterns
 unsigned short *msNumArray; //array of group counts, # of patterns in each hash group 
 int msNumPatterns; //number of patterns loaded
 unsigned msNumHashEntries;
 HASH_TYPE *msHash; //last 2 characters pattern hash table
 unsigned char* msShift; //bad word shift table
 HASH_TYPE *msPrefix; //first 2 characters prefix table
 int msSmallest; //shortest length of all patterns
}WM_STRUCT;
WM_STRUCT * wmNew();
void wmFree(WM_STRUCT *ps);
int wmAddPattern(WM_STRUCT *ps,unsigned char *P,int m);
int wmPrepPatterns(WM_STRUCT *ps);
void wmSearch(WM_STRUCT *ps,unsigned char *Tx,int n);
#endif
WM.c文件如下:
#include 
#include 
#include 
#include
#include "wm.h"
extern int nline=1;
extern int nfound=0;
#define MAXN 10001 //模式串的最大长度MAXN - 1
#define MAXM 51//单词最大长度为MAXM - 1
WM_STRUCT * wmNew()
{
 WM_STRUCT *p=(WM_STRUCT *)malloc(sizeof(WM_STRUCT));
 if(!p) return 0;
 p->msNumPatterns=0;
 p->msSmallest=1000;
 return p;
}
void wmFree(WM_STRUCT *ps)
{
     if(ps->msPatArray)
     {
        if(ps->msPatArray->psPat) free(ps->msPatArray->psPat);
        free(ps->msPatArray );
     }
     if(ps->msNumArray) free(ps->msNumArray);
     if(ps->msHash) free(ps->msHash);
     if(ps->msPrefix) free(ps->msPrefix);
  if(ps->msShift) free(ps->msShift);
     free(ps);
}
int wmAddPattern(WM_STRUCT *ps,unsigned char *q,int m)//m字符串长度
{
 WM_PATTERN_STRUCT *p;
 p=(WM_PATTERN_STRUCT *)malloc(sizeof(WM_PATTERN_STRUCT));
 if(!p) return -1;
 p->psPat=(unsigned char*)malloc(m+1);
 memset(p->psPat+m,0,1);
 memcpy(p->psPat,q,m);
 p->psLen=m;
 ps->msNumPatterns++;
 if(p->psLen<(unsigned)ps->msSmallest) ps->msSmallest=p->psLen;
 p->next=ps->plist;
 ps->plist=p;
 return 0;
}
static unsigned HASH16(unsigned char *T)
{
 return (unsigned short) (((*T)<<8) | *(T+1));
}

void sort(WM_STRUCT *ps)//字符串哈希值从小到大排列
{
 int m=ps->msSmallest;
 int i,j;
 unsigned char *temp;
 int flag;
 for(i=ps->msNumPatterns-1,flag=1;i>0 && flag;i--)
 {
  flag=0;
  for(j=0;j<i;j++)
  {
   if(HASH16(&(ps->msPatArray[j+1].psPat[m-2]))msPatArray[j].psPat[m-2])))
   {
    flag=1;
    temp=ps->msPatArray[j+1].psPat;
    ps->msPatArray[j+1].psPat=ps->msPatArray[j].psPat;
    ps->msPatArray[j].psPat=temp;
   }
  }
 }
}

static void wmPrepHashedPatternGroups(WM_STRUCT *ps)//计算有多少个不同哈希值,且从小到大
{
 unsigned sindex,hindex,ningroup;
 int i;
 int m=ps->msSmallest;
 ps->msNumHashEntries=HASHTABLESIZE;
 ps->msHash=(HASH_TYPE*)malloc(sizeof(HASH_TYPE)* ps->msNumHashEntries);
 if(!ps->msHash)
 {
  printf("No memory in wmPrepHashedPatternGroups()\n");
  return;
 }
 
 for(i=0;i<(int)ps->msNumHashEntries;i++)
 {
  ps->msHash[i]=(HASH_TYPE)-1;
 }
 
 for(i=0;imsNumPatterns;i++)
 {
  hindex=HASH16(&ps->msPatArray[i].psPat[m-2]);
  sindex=ps->msHash[hindex]=i;
  ningroup=1;
  while((++imsNumPatterns) && (hindex==HASH16(&ps->msPatArray[i].psPat[m-2])))
   ningroup++;
  ps->msNumArray[sindex]=ningroup;
  i--;
 }
}

static void wmPrepShiftTable(WM_STRUCT *ps)//建立shift表
{
 int i;
 unsigned short m,k,cindex;
 unsigned shift;
 m=(unsigned short)ps->msSmallest;
 ps->msShift=(unsigned char*)malloc(SHIFTTABLESIZE*sizeof(char));
 if(!ps->msShift)
  return;
 
 for(i=0;i<shifttablesize;i++)
 {
  ps->msShift[i]=(unsigned)(m-2+1);
 }
 
 for(i=0;imsNumPatterns;i++)
 {
  for(k=0;k<m-1;k++)
  {
   shift=(unsigned short)(m-2-k);
   cindex=((ps->msPatArray[i].psPat[k]<<8) | (ps->msPatArray[i].psPat[k+1]));//B为2
   if(shiftmsShift[cindex])
    ps->msShift[cindex]=shift;//k=m-2时,shift=0,
  }
 }
}

static void wmPrepPrefixTable(WM_STRUCT *ps)//建立Prefix表
{
 int i;
 ps->msPrefix=(HASH_TYPE*)malloc(sizeof(HASH_TYPE)* ps->msNumPatterns);
 if(!ps->msPrefix)
 {
  printf("No memory in wmPrepPrefixTable()\n");
  return;
 }
 for(i=0;imsNumPatterns;i++)
 {
  ps->msPrefix[i]=HASH16(ps->msPatArray[i].psPat);
 }
}

void wmGroupMatch(WM_STRUCT *ps,//后缀哈希值相同,比较前缀以及整个字符匹配
     int lindex,
     unsigned char *Tx,
     unsigned char *T)
{
 WM_PATTERN_STRUCT *patrn;
 WM_PATTERN_STRUCT *patrnEnd;
 int text_prefix;
 unsigned char *px,*qx;
 
 patrn=&ps->msPatArray[lindex];
 patrnEnd=patrn+ps->msNumArray[lindex];
 text_prefix=HASH16(T);
 
 for(;patrn<patrnend;patrn++)
 {
  if(ps->msPrefix[lindex++]!=text_prefix)
   continue;
  else
  {
   px=patrn->psPat;
   qx=T;
   while(*(px++)==*(qx++) && *(qx-1)!='\0');
   if(*(px-1)=='\0')
   {
    printf("Match pattern \"%s\" at line %d column %d\n",patrn->psPat,nline,T-Tx+1);
    nfound++;
   }
  }
 }
}

int wmPrepPatterns(WM_STRUCT *ps)//由plist得到msPatArray
{
 int kk;
 WM_PATTERN_STRUCT *plist;
 
 ps->msPatArray=(WM_PATTERN_STRUCT*)malloc(sizeof(WM_PATTERN_STRUCT)*ps->msNumPatterns);
 if(!ps->msPatArray)
  return -1;
 ps->msNumArray=(unsigned short*)malloc(sizeof(short)*ps->msNumPatterns);
 if(!ps->msNumArray)
  return -1;
 
 for(kk=0,plist=ps->plist;plist!=NULL && kkmsNumPatterns;plist=plist->next)
 {
  memcpy(&ps->msPatArray[kk++],plist,sizeof(WM_PATTERN_STRUCT));
 }
 sort(ps);
 wmPrepHashedPatternGroups(ps);
 wmPrepShiftTable(ps);
 wmPrepPrefixTable(ps);
 return 0;
}

void wmSearch(WM_STRUCT *ps,unsigned char *Tx,int n)//字符串查找
{
 int Tleft,lindex,tshift;
 unsigned char *T,*Tend,*window;
 Tleft=n;
 Tend=Tx+n;
 if(nmsSmallest)
  return;
 for(T=Tx,window=Tx+ps->msSmallest-1;window<tend;t++,window++,tleft--)
 {
  tshift=ps->msShift[(*(window-1)<<8) | *window];
  while(tshift)
  {
   window+=tshift;
   T+=tshift;
   Tleft-=tshift;
   if(window>Tend) return;
   tshift=ps->msShift[(*(window-1)<<8) | *window];
  }   
  if((lindex=ps->msHash[(*(window-1)<<8) | *window])==(HASH_TYPE)-1) continue;
  lindex=ps->msHash[(*(window-1)<<8) | *window];
  wmGroupMatch(ps,lindex,Tx,T);
 }
}
int main()
{
 int length,n;
    WM_STRUCT *p;  
    char keyword[MAXM]; //单词
    char str[MAXN]; //模式串
 p=wmNew();
 printf("scanf the number of words-->\n");
    scanf("%d", &n);
 printf("scanf the words-->\n");
    while(n --) 
 {
  scanf("%s", keyword);
  length=strlen(keyword);
  wmAddPattern(p,keyword,length);
    }
 wmPrepPatterns(p);
 printf("scanf the text string-->\n");
 scanf("%s", str);
 length=strlen(str);
 wmSearch(p,str,length);
 wmFree(p);
    return(0);
}
以上程序是从网上搜索来的,但是奇怪的是没有main函数,本人读了两天才把程序写完整。
希望大家交流学习,如有什么问题,可以留言,本人会尽力而为!

猜你喜欢

转载自blog.csdn.net/u011514451/article/details/51488677