KMP学习

详细解析见(http://blog.csdn.net/v_july_v/article/details/7041827)

 

1、

暴力匹配算法

    假设现在我们面临这样一个问题:有一个文本串S,和一个模式串P,现在要查找P在S中的位置,怎么查找呢?

    如果用暴力匹配的思路,并假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,则有:

  • 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
  • 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。
    理清楚了暴力匹配算法的流程及内在的逻辑,咱们可以写出暴力匹配的代码,如下:
[cpp]  view plain  copy
 
  print ?
  1. int ViolentMatch(char* s, char* p)  
    {  
        int sLen = strlen(s);  
        int pLen = strlen(p);  
      
        int i = 0;  
        int j = 0;  
        while (i < sLen && j < pLen)  
        {  
            if (s[i] == p[j])  
            {  
                //①如果当前字符匹配成功(即S[i] == P[j]),则i++,j++      
                i++;  
                j++;  
            }  
            else  
            {  
                //②如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0      
                i = i - j + 1;  
                j = 0;  
            }  
        }  
        //匹配成功,返回模式串p在文本串s中的位置,否则返回-1  
        if (j == pLen)  
            return i - j;  
        else  
            return -1;  
    }  

     

 
 
  1. 2、

  2. 根据《最大长度表》求next 数组

        由上文,我们已经知道,字符串“ABCDABD”各个前缀后缀的最大公共元素长度分别为:

     

     

        而且,根据这个表可以得出下述结论

     

    • 失配时,模式串向右移动的位数为:已匹配字符数 - 失配字符的上一位字符所对应的最大长度值
        上文利用这个表和结论进行匹配时,我们发现,当匹配到一个字符失配时,其实没必要考虑当前失配的字符,更何况我们每次失配时,都是看的失配字符的上一位字符对应的最大长度值。如此,便引出了next 数组。
        给定字符串“ABCDABD”,可求得它的next 数组如下:

     

     

     

        把next 数组跟之前求得的最大长度表对比后,不难发现,next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。意识到了这一点,你会惊呼原来next 数组的求解竟然如此简单:就是找最大对称长度的前缀后缀,然后整体右移一位,初值赋为-1(当然,你也可以直接计算某个字符对应的next值,就是看这个字符之前的字符串中有多大长度的相同前缀后缀)。

        换言之,对于给定的模式串:ABCDABD,它的最大长度表及next 数组分别如下:

        根据最大长度表求出了next 数组后,从而有

     

    失配时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值
    1. void GetNext(char* p,int next[])  
      {  
          int pLen = strlen(p);  
          next[0] = -1;  
          int k = -1;  
          int j = 0;  
          while (j < pLen - 1)  
          {  
              //p[k]表示前缀,p[j]表示后缀  
              if (k == -1 || p[j] == p[k])   
              {  
                  ++k;  
                  ++j;  
                  next[j] = k;  
              }  
              else   
              {  
                  k = next[k];  
              }  
          }  
      }  

       

 
 
 
  1. //优化过后的next 数组求法  
    void GetNextval(char* p, int next[])  
    {  
        int pLen = strlen(p);  
        next[0] = -1;  
        int k = -1;  
        int j = 0;  
        while (j < pLen - 1)  
        {  
            //p[k]表示前缀,p[j]表示后缀    
            if (k == -1 || p[j] == p[k])  
            {  
                ++j;  
                ++k;  
                //较之前next数组求法,改动在下面4行  
                if (p[j] != p[k])  
                    next[j] = k;   //之前只有这一行  
                else  
                    //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]  
                    next[j] = next[k];  
            }  
            else  
            {  
                k = next[k];  
            }  
        }  
    }  
    

     

 
3、KMP模板
  1. int KmpSearch(char* s, char* p)  
    
    {  
        int i = 0;
      
        int j = 0;  
    
        int sLen = strlen(s);
      
        int pLen = strlen(p);  
    
        while (i < sLen && j < pLen)  
    
        {  
           
    
     //①如果j = -1,或者当前字符匹配成功(即
    
    S[i] == P[j]),都令i++,j++   
    
       
            if (j == -1 || s[i] == p[j]) 
     
            {  
                i++;  
    
                j++;
      
            }
      
            else 
     
            {  
                
    //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),
    
    则令 i 不变,j = next[j] 
    
         
                
        
            j = next[j];  //next[j]即为j所对应的next值    
    
            }  
    
        } 
     
        if (j == pLen)  
    
            return i - j;
      
        else 
     
            return -1;
    
      
    }  
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值