WM算法--多模式匹配

来源:互联网 发布:云计算实验室建设方案 编辑:程序博客网 时间:2024/05/01 23:15
 

WM算法详解

WM算法采用字符块技术,增大了主串和模式串不匹配的可能性,从而增加了直接跳跃的机会。使用散列表选择模式串集合中的一个子集与当前文本进行完全匹配。使用前缀表进一步过滤不匹配的模式串,使算法获得了较高的运行效率。

WM算法首先对模式串集合进行预处理。预处理阶段将建立3个表格:SHIFT表,HASH表和PREFIX表。SHIFT表用于在扫描文本串的时候,根据读入字符串决定可以跳过的字符数,如果相应的跳跃值为0,则说明可能产生匹配。HASH表用来存储尾块字符散列值相同的模式串。PREFIX表用于存储尾块字符散列值相同的模式串的首块字符散列值。

假设模式串集合P中最短的模式长度为m,那么,后续仅考虑所有模式的前m个字符组成的模式串。

设X=x1…xB为T中的待比较的长度为B的子串,通过hash函数映像得到一个索引值index,以该索引值作为偏移得到SHIFT表中的值,该值决定读到当前子串x后可以跳过的位数。假设X映射到SHIFT表的入口为index的表项,即index=hash(x)。

SHIFT表中值的计算原则为:

(1)       如果X不出现在模式串中,则SHIFT[h]=m-B+1。

(2)       如果X出现在某些模式串中,而且在所有的模式串中的最右出现位置为q,则SHIFT[h]=m-q。

算法匹配的大致原理:

(1)       设当前比较的文本串X的hash值为h。如果SHIFT[h]=0,说明可能产生了匹配,那么需要进一步的判断。

(2)       用该h值作为索引,查HASH表找到HASH[h],它存储的是指标,指向两个单独的表:一个是模式链表,另一个是PREFIX表。模式链表中存放的是后B个字符的hash值同为h的所有模式。

(3)       对于待比较长度为m的串,如果其长度为B的前缀与模式的前缀的hash值也相同,则再将相应的文本串与符合的模式逐一进行比较,最终判定是否完全匹配。

算法匹配的主要过程:

基于后缀的模式匹配,每次扫描B个字符T[m-B+1]…..T[m]。

(1)       扫描text的末B位T[m-B+1]…..T[m]通过hash function计算其哈希值h。

(2)       查表,SHIFT[h]>0,text小指针后滑SHIFT[h]位,执行(1);SHIFT[h]=0,执行那个(3)。

(3)       计算此m位text的前缀的哈希值,记为text_prefix。

(4)       对于每个p(HASH[h]≦p<HASH[h+1]),看是否PREFIX[p]=text_prefix。

(5)       如果相等,让真正的模式串去和text匹配。

 

  WM算法的代码网上已经有了,下面给出其中的一个参

考:

http://www.nhs8.com/index.php/post/1338.htm

l

    这段代码对于初学者可能还是比较的晦涩,下面给出

对这份代码的详细注释以及流程图。

    首先给出的是对代码的详细注释:

 

  

#ifndef WM_H

#define WM_H

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#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//模式串转变为WM _PATTERN_STRUCT后的结构

unsigned short *msNumArray; //array of group counts, # of patterns in each hash group

int msNumPatterns; //number of patterns loaded//模式串的个数

unsigned msNumHashEntries;//HASH表的大小

HASH_TYPE *msHash; //last 2 characters pattern hash table//HASH表

unsigned char* msShift; //bad word shift table//SHIFT表

HASH_TYPE *msPrefix; //first 2 characters prefix table//PREFIX表

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);

#endifWM.c文件如下:view plaincopy to

clipboardprint?

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <ctype.h>

#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_STRUCT结构

{

 

/>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); //模式串最后一位位0,

结束标志

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]))<HASH16(&(ps->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;i<ps->msNumPatterns;i++)

{

hindex=HASH16(&ps->msPatArray[i].psPat[m-

2]);

sindex=ps->msHash[hindex]=i;

ningroup=1;

//此时哈希表已经有序了

while((++i<ps->msNumPatterns) &&

(hindex==HASH16(&ps->msPatArray[i].psPat

[m-2])))

ningroup++;

ps->msNumArray[sindex]=ningroup;

i–;

}

}

static void wmPrepShiftTable(WM_STRUCT

*ps)//建立shift表,算出每个字符块要移动的距离,像

Horspool算法那样的

{

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;i<ps->msNumPatterns;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(shift<ps->msShift[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;i<ps->msNumPatterns;i++)

{

ps->msPrefix[i]=HASH16(ps->msPatArray

[i].psPat); //对每个模式串的前缀进行哈希

}

}

void wmGroupMatch(WM_STRUCT *ps,//后缀哈希

值相同,比较前缀以及整个字符匹配

int lindex, //lindex为后缀哈希值相同的那些模式串中

的一个模式串的index

unsigned char *Tx,

unsigned char *T)

{

WM_PATTERN_STRUCT *patrn;

WM_PATTERN_STRUCT *patrnEnd;

// int text_prefix;

HASH_TYPE text_prefix; //changed by dklkt

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 &&

kk<ps->msNumPatterns;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(n<ps->msSmallest)

return;

//T--------窗口-------Window

for(T=Tx,window=Tx+ps->msSmallest-

1;window<Tend;T++,window++,Tleft–)

{

tshift=ps->msShift[(*(window-1)<<8) |

*window];

while(tshift) //此时tshift!=0,无匹配

{

window+=tshift;

T+=tshift;

Tleft-=tshift;

if(window>Tend) return;

tshift=ps->msShift[(*(window-1)<<8) |

*window];

}

//tshift=0,表明后缀哈希值已经相同

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()

{

WM_STRUCT* p=wmNew();--->(1)

While(n--)

    wmAddPattern(p,keyword,klength);--->(2)

wmPrepPatterns(p);---->(3)

wmSearch(p,str,slength);----->(8)

wmFree(p);---->(10)

}

 

(1)    new(p)以及p的初始化

(2)    keyword-----add-->p->list

(3)    plist---得到->msPatArray

sort(ps);-->(4)

wmPrepHashedpatternsGroup(ps);-->(5)

wmPrepShiftTable(ps);-->(6)

wmPrepPrefixTable(ps);--->(7)

(4)    冒泡排序,字符串哈希值从小到大排列

(5)    HASH表初始化

计算有多少个不同哈希值,且从小到大

拉链:

(6)    构建SHIFT表

SHIFT[index]=  m-B+1;//无匹配

               min(shift);//最短移动距离

(7)    构建PREFIX表

Ps->msPrefix[i]=HASH16(ps->msPatArray[i].psPat);

(8)    求tshift

若tshift!=0  移动tshift

若tshift=0   hash值不存在,continue;

            wmGroupMatch--->(9)

(9)    for(;patrn<patrnEnd;patrn++)

      比较ps->msprefix[index++]和text_prefix

      若相等,再比较整个模式串

(10) free(p)

 

 

 

原创粉丝点击