• KMP算法简单应用


    K(看)M(毛)P(片)算法最常用在字符串匹配。给定一个长的字符串(target string)和一个短的字符串(pattern string),要求判断pattern string是否是target string的子串,如果是,则返回子串的首个字符的下标;如果否,则返回-1。

    解决这个问题最常想到的办法就是brutal force,即从target string第一个字符开始与pattern string比较,如果相等则比较target string和pattern string的下一个字符,如果不等则返回到target string中相等的字符的下一个字符。换句话说,假设我们用target和pattern分别表示两个字符串的指针,那么每一次比较不管两个string匹配到何种程度,只要不是完全匹配(即匹配完成),那么target永远只能增加1,这个算法的复杂度为O(mn).(m=strlen(target string),n=strlen(pattern string)).

    以下是这个算法的C代码。

     1 int strstr(char *target, char *pattern)
     2 {
     3   int i,j;
     4   for(i=0;;i++)
     5     {
     6         for(j=0;;j++)
     7         {
     8             if(pattern[j]==0)return i;
     9             if(target[i+j]==0)return -1;
    10             if(target[i+j]!=pattern[j])break;
    11         }
    12     }
    13 }

    导致这个算法时间复杂的关键,在于我们每次只能将target指针加一,而不能充分利用之前已匹配部分的信息。一个很好的例子由http://kenby.iteye.com/blog/1025599 给出,事实上我们可以通过利用已匹配部分的信息,让每次比较失败后target跳过多个位置。

    下面给出KMP的代码再给出一些解释。

     1 int* overlay(char *str)
     2 {
     3     int *a,len=strlen(str),i,index;
     4     a[0]=-1;//美好的约定
     5     a=(int *)calloc(len,sizeof(int));
     6     for(i=1;i<len;i++)
     7     {
     8         index=a[i-1];//上一个
     9         while(index>=0&&a[index+1]!=a[i])
    10         {
    11             index=a[index];
    12         }
    13         if(a[index+1]==a[i])
    14         {
    15             a[i]=index+1;
    16         }
    17         else a[i]=-1;
    18     }
    19     return a;
    20 }
    21 
    22 int strstr(char *target,char *pattern)
    23 {
    24     int i,j,*a,len1=strlen(target),len2=strlen(pattern);
    25     a=overlay(pattern);
    26     for(i=0,j=0;i<len1&&j<len2;)
    27     {
    28         if(target[i]==pattern[j])
    29         {
    30             i++;
    31             j++;
    32         }
    33         else if(j==0)i++;//若第一个字符就不相等,则对target的指针加1即可
    34         else j=a[j-1]+1;
    35     }
    36     if(j==len2)return i-j;
    37     else return -1;
    38 }

    overlay函数是用递推求出pattern串每一个位置对应的覆盖值,算法在http://blog.csdn.net/power721/article/details/6132380有解释,不赘述。覆盖值数组的含义在上述博客中没有解释得很清楚,我举个例子。

    首先假设这个字符串名为str。首字符的覆盖值是-1,这是约定。从第二个字符开始,我们看到b的覆盖值是-1,什么意思呢,意思就是在b之前的子串(包括b),不存在k满足str[0]str[1]...str[k-1]str[k]=str[1-k]str[2-k]...str[1]。接下来,第三个字符a的覆盖值为0,表示在a之前的子串(包括a),存在k=0使得str[0]str[1]...str[k-1]str[k]=str[2-k]str[3-k]...str[2]。第五个字符b的覆盖值为1,表示在b之前的子串(包括b),存在k=1使得str[0]str[1]...str[k-1]str[k]=str[4-k]str[5-k]...str[4]。

    至于overlay函数的递推求法,博客里有说明递推过程,我也是看了好一会才看清楚orz...下面配个图,图里中括号里面是相同的字符串,小括号里面也是相等的字符串,以此第三次,第四次一直往下找...大家才思敏捷,一定可以看得懂。

    其余的东西在以上两篇博客都讲得很清楚,我也不再赘述。算法导论里有关于KMP算法摊还分析的一些证明,在不同的地方,上文所述的覆盖值的含义有可能有一点点差别,命名也有不同,但总的意思和算法都是差不多的。

    部分代码参考了https://leetcode.com/problems/implement-strstr/?tab=Description

  • 相关阅读:
    java.lang.IllegalArgumentException: node to traverse cannot be null!
    c3p0连接池的使用
    eclipse插件
    eclipse字体颜色设置
    oracle增删改查
    resultMap / resultType
    oracle 序列 ,check约束
    JSP:一种服务器端动态页面技术的组件规范。
    js
    字体
  • 原文地址:https://www.cnblogs.com/KRCheung/p/6473379.html
Copyright © 2020-2023  润新知