`

KMP算法,及在内核的实现

阅读更多

KMP算法的实现

转自 http://www.cppblog.com/converse/    希望创系兄不要生气
KMP算法是一种用于字符串匹配的算法,这个算法的高效之处在于当在某个位置匹配不成功的时候可以根据之前的匹配结果从模式 字符串的另一个位置开始,而不必从头开始匹配字符串.
因此这个算法的关键在于,当某个位置的匹配不成功的时候,应该从模式字符串的哪一个位置开始 新的比较.假设这个值存放在一个next数组中,其中next数组中的元素满足这个条件:next[j] = k,表示的是当模式字符串中的第j + 1个(这里是遵守标准C语言中数组元素从0开始的约定,以下不再说明)发生匹配不成功的情况时,应该从模式字符串的第k + 1个字符开始新的匹配.如果已经得到了模式字符串的next数组,那么KMP算法的实现如下:
// KMP字符串模式匹配算法
// 输入: S是主串,T是模式串,pos是S中的起始位置
// 输出: 如果匹配成功返回起始位置,否则返回-1
int KMP(PString S, PString T, int pos)
{
    assert(NULL != S);
    assert(NULL != T);
    assert(pos >= 0);
    assert(pos < S->length);
   
    if (S->length < T->length)
        return -1;
    printf("主串\t = %s\n", S->str);
    printf("模式串\t = %s\n", T->str);
    int *next = (int *)malloc(T->length * sizeof(int));
    // 得到模式串的next数组
    GetNextArray(T, next);
    int i, j;
    for (i = pos, j = 0; i < S->length && j < T->length; )
    {
        // i是主串游标,j是模式串游标
        if (-1 == j ||                // 模式串游标已经回退到第一个位置
            S->str[i] == T->str[j]) // 当前字符匹配成功
        {
            // 满足以上两种情况时两个游标都要向前进一步
            ++i;
            ++j;
        }
        else                        //  匹配不成功,模式串游标回退到当前字符的next值
        {
            j = next[j];
        }
    }
    free(next);
    if (j >= T->length)
    {
        // 匹配成功
        return i - T->length;
    }
    else
    {
        // 匹配不成功
        return -1;
    }
}
下面看看如何得到next数组.
这是一个递推求解的过程,初始的情况是next[0] = -1.
假设在某一个时刻有如下的等 式成立:str[0...k-1] = str[j - k...j - 1],那么next[j] = k,在这个前提下,继续进行下一个字符的匹配.
1) 如果str[0...k] = str[j - k...j],那么next[j + 1] = next[j] + 1 = k + 1.
2) 反之,如果上面的匹配不成立,那么就要从next[k]开始进行新的匹配,如果成功的话,那么:
next[j + 1] = next[next[j]] + 1 = next[k] + 1;
如果还是不能匹配成功就再从next[next[k]]的位置开始进行的新的 匹配,直到匹配成功为止.如果这个过程一直进行下去都没有找到可以成功匹配的字符的话,那么next[j + 1] = 0,这时表示要从字符串的第一个位置开始新的匹配了.
用一个公式表示上述的算法,那么可以写作:
next[j] =
1)-1, 当j = 0时;
2) Max{k | 0 <= k < j && str[0..k - 1] = str[j - k...j - 1]};
3)0,其他情况,此时匹配要从第一个位置重新开始.

寻找next数组的算法如下:
 //  得到字符串的next数组
 void  GetNextArray(PString pstr,  int  next[])
  {
    assert(NULL  !=  pstr);
    assert(NULL  !=  next);
    assert(pstr -> length  >   0 );
     //  第一个字符的next值是-1,因为C中的数组是从0开始的
     next[ 0 ]  =   - 1 ;
     for  ( int  i  =   0 , j  =   - 1 ; i  <  pstr -> length  -   1 ; )
      {
         //  i是主串的游标,j是模式串的游标
         //  这里的主串和模式串都是同一个字符串
          if  ( - 1   ==  j  ||                          //  如果模式串游标已经回退到第一个字符
             pstr -> str[i]  ==  pstr -> str[j])     //  如果匹配成功
            {
             //  两个游标都向前走一步
              ++ i;
             ++ j;
             //  存放当前的next值为此时模式串的游标值
             next[i]  =  j;
         /*
             此处有一个改进算法:   if(T[i] !=T[j] )  next[i]=j;
                       else       next[i]=next[j];
            
             */
        }
         else                                  //  匹配不成功j就回退到上一个next值
            {
            j  =  next[j];
        }
    }
}
 
完整的算法如下:
  /**/ /* *******************************************************************
    created:    2006/07/02
    filename:     KMP.cpp
    author:        李创
                 http://www.cppblog.com/converse/
               
                参考资料: 严蔚敏<<数据结构>>
    purpose:    KMP字符串匹配算法的演示
******************************************************************** */
 
#include  < stdio.h >
#include  < stdlib.h >
#include  < assert.h >
#include  < string .h >
 
 #define  MAX_LEN_OF_STR    30             //  字符串的最大长度
 
typedef  struct  String                 //  这里需要的字符串数组,存放字符串及其长度
   {
     char     str[MAX_LEN_OF_STR];     //  字符数组
      int         length;                     //  字符串的实际长度
 } String,  * PString;
 //  得到字符串的next数组
 void  GetNextArray(PString pstr,  int  next[])
  {
    assert(NULL  !=  pstr);
    assert(NULL  !=  next);
    assert(pstr -> length  >   0 );
     //  第一个字符的next值是-1,因为C中的数组是从0开始的
     next[ 0 ]  =   - 1 ;
     for  ( int  i  =   0 , j  =   - 1 ; i  <  pstr -> length  -   1 ; )
      {
         //  i是主串的游标,j是模式串的游标
         //  这里的主串和模式串都是同一个字符串
          if  ( - 1   ==  j  ||                          //  如果模式串游标已经回退到第一个字符
             pstr -> str[i]  ==  pstr -> str[j])     //  如果匹配成功
            {
             //  两个游标都向前走一步
              ++ i;
             ++ j;
             //  存放当前的next值为此时模式串的游标值
             next[i]  =  j;
        }
         else                                  //  匹配不成功j就回退到上一个next值
            {
            j  =  next[j];
        }
    }
}
 
 //  KMP字符串模式匹配算法
 //  输入: S是主串,T是模式串,pos是S中的起始位置
 //  输出: 如果匹配成功返回起始位置,否则返回-1
 int  KMP(PString S, PString T,  int  pos)
  {
    assert(NULL  !=  S);
    assert(NULL  !=  T);
    assert(pos  >=   0 );
    assert(pos  <  S -> length);
   
     if  (S -> length  <  T -> length)
         return   - 1 ;
    printf( " 主串\t = %s\n " , S -> str);
    printf( " 模式串\t = %s\n " , T -> str);
     int   * next  =  ( int   * )malloc(T -> length  *   sizeof ( int ));
     //  得到模式串的next数组
     GetNextArray(T, next);
     int  i, j;
     for  (i  =  pos, j  =   0 ; i  <  S -> length  &&  j  <  T -> length; )
      {
         //  i是主串游标,j是模式串游标
          if  ( - 1   ==  j  ||                  //  模式串游标已经回退到第一个位置
             S -> str[i]  ==  T -> str[j])  //  当前字符匹配成功
            {
             //  满足以上两种情况时两个游标都要向前进一步
              ++ i;
             ++ j;
        }
         else                          //   匹配不成功,模式串游标回退到当前字符的next值
            {
            j  =  next[j];
        }
    }
 
    free(next);
     if  (j  >=  T -> length)
      {
         //  匹配成功
          return  i  -  T -> length;
    }
     else
       {
         //  匹配不成功
          return   - 1 ;
    }
}

在内核使用时做的修改

typedef struct KString //  这里需要的字符串数组,存放字符串及其长度
{
    char *str; //  字符串指针
    int length; //  字符串的实际长度
} KString, *PString;
//  KMP字符串模式匹配算法
//  输入: S是主串,T是模式串,pos是S中的起始位置,next数组,不小于模式串长度
//  输出: 如果匹配成功返回起始位置,否则返回-1
int KMP(PString S, PString T, int pos,int next[]);

//  得到字符串的next数组
void GetNextArray(PString pstr, int next[])
{
/*    assert(NULL != pstr);
    assert(NULL != next);
    assert(pstr -> length > 0 );*/

    //  第一个字符的next值是-1,因为C中的数组是从0开始的
    int i,j;
    next[0] = -1;

    for (i = 0, j = -1; i < pstr -> length - 1;)
    {
        //  i是主串的游标,j是模式串的游标
        //  这里的主串和模式串都是同一个字符串
        if (-1 == j || //  如果模式串游标已经回退到第一个字符
                pstr -> str[i] == pstr -> str[j]) //  如果匹配成功
        {
            //  两个游标都向前走一步
            ++i;
            ++j;
            //  存放当前的next值为此时模式串的游标值
            next[i] = j;
        }
        else //  匹配不成功j就回退到上一个next值
        {
            j = next[j];
        }
    }
}

//  KMP字符串模式匹配算法
//  输入: S是主串,T是模式串,pos是S中的起始位置,next数组,不小于模式串长度
//  输出: 如果匹配成功返回起始位置,否则返回-1
int KMP(PString S, PString T, int pos,int next[])
{
    int i, j;
    if (S == NULL)
    {
        return -1;
    }
    if (T == NULL)
    {
        return -1;
    }
    if (pos < 0 || pos >= S -> length)
    {
        return -1;
    }
    if (S -> length < T -> length)
        return -1;
    //printf( " 主串\t = %s\n " , S -> str);
    //printf( " 模式串\t = %s\n " , T -> str);
    //int * next = (int *) malloc(T -> length * sizeof(int));
    //  得到模式串的next数组
    GetNextArray(T, next);

    for (i = pos, j = 0; i < S -> length && j < T -> length;)
    {
        //  i是主串游标,j是模式串游标
        if (-1 == j || //  模式串游标已经回退到第一个位置
                S -> str[i] == T -> str[j]) //  当前字符匹配成功
        {
            //  满足以上两种情况时两个游标都要向前进一步
            ++i;
            ++j;
        }
        else //   匹配不成功,模式串游标回退到当前字符的next值
        {
            j = next[j];
        }
    }

    //free(next);
    if (j >= T -> length)
    {
        //  匹配成功
        return i - T -> length;
    }
    else
    {
        //  匹配不成功
        return -1;
    }
}
int main()
{
    int n = -1;
    String
            ms =
                    {
                            "/wwwFlv/flv/045/602/630/45602630.1863de444576803f6aebe03fd5dbb9854e94b2c7_238_6.flv?11000&key=716fb05bbaa688cc8ebccc4c3d7f4f068e3fd2&id=tudou&itemid=25047593&fi=47611925&sz=210424835&posky=hCsbR0JyXfxHcP9h27IK15783ihgv",
                            218 };
    String ss =
    { ".flv", 4 };
    int lnext[4];
    n = KMP2((PString) &ms, (PString) &ss, 0,lnext);
    printf("%d", n);
    return 0;
}
  • kmp.gz (1.4 KB)
  • 下载次数: 4
分享到:
评论

相关推荐

    十五个经典算法研究与总结

    KMP算法(Knuth-Morris-Pratt算法)是一种字符串匹配算法,能够在O(m+n)时间内完成模式串和主串的匹配。 **KMP算法的关键点:** - **前缀表**:预先计算出模式串的前缀表,记录最长的相同前后缀长度。 - **失配...

    C语言经典算法100例.pdf

    - 字符串匹配算法(如KMP算法)可以大大提高字符串搜索的效率。 - 字符串分割、反转等常见操作也经常被用作面试题目。 6. **递归与迭代**: - 许多算法问题可以通过递归来简化逻辑,但同时也要注意递归可能导致...

    c数据结构及算法经典

    8. **字符串处理**:KMP算法、Rabin-Karp算法、Boyer-Moore算法等,用于字符串匹配。 9. **递归与分治**:递归是函数直接或间接调用自身,常用于解决复杂问题,如归并排序、快速排序等。分治策略将大问题分解为小...

    实际项目中的常见算法.docx

    18. **Knuth-Morris-Pratt(KMP)字符串匹配**:KMP算法是一种线性时间复杂度的字符串匹配算法,广泛应用于文本处理和搜索。 19. **Boyer-Moore字符串匹配**:Boyer-Moore算法通过跳过不可能匹配的部分,提高了搜索...

    数据结构与算法

    - **串的模式匹配算法**:例如KMP算法用于快速查找字符串中的子串。 - **字符串的排列**:生成所有可能的排列组合。 #### 第7章 数组和广义表 - **数组**:一种基本的数据结构,可以高效地存储和访问数据。 - **...

    软件开发中的十五个经典算法研究

    KMP算法(Knuth-Morris-Pratt算法)是一种高效字符串匹配算法,能够在O(n + m)时间内完成模式串m在文本串n中的匹配。 **关键特性**: - **部分匹配表**:预处理模式串得到的部分匹配表可以避免不必要的字符比较。 ...

    数据结构算法

    #### 六、KMP算法 Knuth-Morris-Pratt (KMP)算法是一种字符串匹配算法,能够在O(n+m)的时间复杂度内完成模式串和主串之间的匹配,其中n是主串长度,m是模式串长度。 **特性与应用:** - **高效性**:避免了传统...

    GPU进行字符串匹配

    字符串匹配算法有多种,例如BF算法(Brute Force,暴力匹配算法)、KMP算法(Knuth-Morris-Pratt)、RK算法(Rabin-Karp算法)等。GPU通过SIMD(Single Instruction Multiple Data,单指令多数据)架构对图像数据...

    北京邮电大学807 软件工程专业综合2021年考试专业课初试大纲.pdf

    - **字符串匹配**:学习KMP算法等字符串匹配算法。 5. **树和森林** - **树的概念**:理解树的基本结构和主要概念,熟悉二叉树的结构及其特点。 - **二叉树遍历**:掌握二叉树的三种遍历方法的实现原理和性质,...

    ARM二进制敏感性数据检测方法研究.pdf

    在讨论中,本文指出了传统单模式匹配算法的局限性,这些算法包括BF(暴力匹配)、KMP、BM及Sunday算法等。这些算法在处理大量恶意代码时,效率低下,且容易受到加密技术的干扰,降低了检测的准确性。为此,文章提出...

    蘑菇街2017校园招聘笔试题及答案.pdf

    10. **KMP算法**:KMP是一种字符串匹配算法,时间复杂度为O(M+N),其中M是模式串长度,N是文本串长度。 11. **排序算法**:插入排序是一种简单的排序算法,通过构建有序序列,将未排序的元素插入到已排序序列的合适...

    2019年12月清华计算机912题目.docx

    - **KMP算法**:模式匹配算法,避免了不必要的回溯,提高了字符串匹配效率。 - **Huffman编码**:一种无损数据压缩算法,通过构建最优的二叉树来实现字符的高效编码。 2. **计算机组成原理** - **流水线技术**:...

    C语言学习资料(学C语言的必看).pdf

    2. **C语言程序设计**:通过学习经典C源程序,如快速排序、全排列的递归算法、KMP字符串搜索算法,可以了解C语言在算法实现中的应用。这些实例有助于提高编程能力和算法思维。 3. **数据结构与算法**:数据结构如...

    408Q&A.docx

    KMP算法实现原理 - **KMP算法**:一种改进的字符串匹配算法,核心在于构建next数组,减少模式串的回溯次数,提高匹配效率。 #### 5. 哈夫曼编码原理 - **哈夫曼编码**:一种用于数据压缩的前缀编码方法,通过构造...

Global site tag (gtag.js) - Google Analytics