so true

心怀未来,开创未来!
随笔 - 160, 文章 - 0, 评论 - 40, 引用 - 0
数据加载中……

KMP算法的改进之一

#include <iostream>
using namespace std;

//该结构体是一个(index,value)的值对
struct Pair{
 Pair(int i,int v):index(i),value(v),next(NULL){}
 int index;
 int value;
 Pair* next;
};
void getNext(char *match, int next[], int len){
 Pair *phead=NULL;//存储发生变异的值对的头结点
 next[0]=-1;
 for(int j=1,k;j<len;j++){//依次填充next数组
  k=next[j-1];//从next[j-1]出发,去递推next[j]的值
  while(k>-1 && match[k]!=match[j-1])k=next[k];//依次递推
  next[j]= k+1;//存储next[j]的值

  while(k>-1 && match[j]==match[k+1])k=next[k];
  /*
  这样二次搜寻的目的是为了避免重复,分析如下:

  记文本串中的当前失配字符为X,即X!=match[j];
  如果根据next[j]找到的再次比较位置满足match[j]=match[next[j]]=match[k+1],
  那么显然有X!=match[k+1],因此对于此种情况,找到的next[j]位置是无效的,需要继续查找。
  但是,找到的新结果不能覆盖next[j]当前值,否则将影响了next[j]的含义(在模式串match中,
  当前位置j之前的next[j]-1个字符完全等价于模式串头部的next[j]-1个字符,且next[j]-1这个长度
  已经最大,不能再继续增长),将导致不能被后续的递推过程利用。因此必须要临时存放到其他
  位置,考虑到这种情况出现的可能性较低,因此为其分配一个len长的数组存储会
  相当浪费空间,所以使用链表来做,链表的每个结点保存的是一个(index,value)的值对!
   */
  if(next[j]!=k+1)//将新结果插入到链表头结点之前,好处有如下两点:
  //1。不插入到尾部,而是插入到头结点之前,可以避免每次插入之前对链表的遍历搜寻
  //2。还不必考虑头结点是否为NULL
  {
   Pair *p=new Pair(j,k+1);
   p->next=phead;
   phead=p;
  }
 }
 while(phead!=NULL){//依次将链表中的结点内容拿出来更新next数组
  next[phead->index]=phead->value;
  Pair *p=phead;//为了下面释放资源
  phead=phead->next;
  delete p;
 }
 //next[0]=0;

 /*
 这几行代码完成了经典的KMP算法中对next数组的求解
 //这个算法的关键之处:求next[j],和match[j]没有半点关系,
 //却和match[j-1]有着莫大的关系,关键就是检查match[j-1]这个元素到底和递推过程中的哪一个match[next[k]]相等
 //也就是在考虑“到底j之前能有多少个元素依次与模式串头部开始的字符一一匹配呢?”,最终的答案是next[k]+1,
 //实则代表了j为之前有k个字符能与串头部的k个字符一一匹配。
 //这k个字符应该分成这样一种结构 1+(next[k']-1),这里k=next[k'],
 //第一个1代表最终要确保成立的“match[j-1]=match[k]”,而“next[k']-1”代表着k'位置之前有k-1个字符与串头部一一匹配
 //而这句话完全可以等价于“代表着j-1位置之前有k-1个字符与串头部一一匹配”,
 //这句话很难理解,需要对照建立next数组的那个图仔细研究一下,相信大家都能最终理解这句话。
 //因此求解next[j],我们只需关心两件事既可:
 //(1)在j-1这个位置上到底能不能为最终结果贡献这个1
 //(2)在(1)满足的情况下,在j-1之前到底是多少个字符完全与串头一一匹配,即为最终结果贡献这个k-1
 next[0]=-1;
 for(int j=1,k;j<len;j++){
  k=next[j-1];//递推的起点
  while(k>-1 && match[k]!=match[j-1] )k=next[k];//递推的过程
  next[j]= k+1;//递推的结果
 }
  */
}

void match_string(char * match, char* text){
 int len_match=strlen(match);
 int *pn=new int[len_match];
 getNext(match,pn,len_match);

 //第一种搜寻的方法
 int j=0;
 while(*text!=0){
  if(*text==match[j]){
   text++;
   j++;
   if(match[j]==0)
   {
    cout<<text-len_match<<endl;
    j=0;
   }
  }else{
   j=pn[j];
   if(j==-1){
    text++;
    j=0;
   }
  }
 }


 /*
 //第二种搜寻的方法
 int i=0;
  while(*text!=0){
   if(i==len_match){
    cout<<text-len_match<<endl;
    i=0;
   }
   if(*text++==match[i++])continue;
   else{
    i=pn[i-1];
    if(i==-1){i=0;continue;}
    text--;
   }
  }*/
 
 
 delete [] pn;
}

void main(){
 char *match="abc";
 char *text="aabcdabcabcxab";
 match_string(match,text);
}
/*输出结果为:
abcdabcabcxab
abcabcxab
abcxab
*/

posted on 2008-10-20 22:21 so true 阅读(305) 评论(0)  编辑  收藏 所属分类: C&C++


只有注册用户登录后才能发表评论。


网站导航: