zoukankan      html  css  js  c++  java
  • KMP算法

    KMP算法

    一、理论

      参考于:http://www.cnblogs.com/hxsyl/p/3300344.html

      先介绍前缀数组是如何产生的。首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

        来看一个例子:chi表示模式串的前i个字符组成的前缀, next[i] = j表示chi中的开始j个字符和末尾j个字符是一样的(注意下标是字符数目),而且对于前缀chi来说,这样的j是最大值。next[i] = j的另外一个定义是:有一个含有j个字符的串,它既是chi的真前缀,又是chi的真后缀。 

        规定:next[1] = next[0] = 0,这个规定不像0!=1那样,而是确实是这样子,不懂得看上面的前后缀概念。注意:next数组里并不是首尾回文串,而是前缀等于后缀,理解这个对于递推求next数组很重要哟。next[i]就是前缀数组,下面通过1个例子来看如何构造前缀数组。 

         例:cacca有5个前缀,求出其对应的next数组。前缀2为ca,显然首尾没有相同的字符,next[2] = 0,前缀3为cac,显然首尾有共同的字符c,故next[3] = 1,前缀4为cacc,首尾有共同的字符c,故next[4] = 1,前缀5为cacca,首尾有共同的字符ca,故next[5] = 2。如果仔细观察,可以发现构造next[i]的时候,可以利用next[i-1]的结果。比如abcdabc,模式已求得next[7] = 3,为求next[8],可以直接比较第4个字符和第8个字符,如果它们相等,则next[8] = next[7]+1 = 4,这是因为next[7] = 3保证了前缀ch7的末尾4个字符的前3个字符是一样的。但如果这两个字符不想等呢?那就继续迭代,利用(k=3)k = next[k]的值来求,直到k=0(next[8] = 0)或者字符相等(next[8] = k+1)。

    二、例子与实现

      已知模式字符串P[1..m]与文本字符串T[1..n],且已知模式字符P[1..q]与文本字符T[s+1..s+q]匹配,那么满足:P[1..k]=T[s1+1..s1+k],其中s1+k=s+qk是满足条件的最大值,如下图所示。

     

      从图中可以看出,如果P[q+1]T[s+q+1]不匹配,无需把P[1..k]T[s1+1..s1+k]的字符先比较,由公式P[1..k]=T[s1+1..s1+k]可知它们肯定是匹配的,同样如果拿P[k+1]T[s+q+1]比较,如果不匹配,则用k中的前k1(k1满足P[1..k1]=T[s2+1..s2+k1])个当做k,来比较P[k+1]T[s+q+1],一直下去,直到P[k+1]= T[s+q+1]

    可以用模式与自身进行比较,在指定匹配长度为q的条件下,预先计算出k的值。

    具体实现:

      q为匹配字符的长度,k为满足等式中P[1..k]=T[s1+1..s1+k]的前缀最长长度值。prefix_func[]存放在匹配了长度为q的条件下k的值。通过prefix_func[],就可以依据相同的算法获得P对T的模式匹配,如下面的KMP算法。

     1 int next[100];
     2 
     3 void GetNext(char *t)
     4 {
     5     int i = 0, j = -1;  //注意j=-1
     6 
     7     if (t == NULL)
     8         return;
     9     next[0] = -1;
    10     while (t[i])
    11     {
    12         if (j == -1 || t[i] == t[j])  //为了获得next[i+1],默认next[1]=0    
    13         {
    14             i++;
    15             j++;
    16             next[i] = j;
    17         }
    18         else
    19             j = next[j];
    20     }
    21 }
    22 int Kmp(char *s, char *t)
    23 {
    24     int i = 0, j = 0;  //注意i和j的初值
    25 
    26     if (s == NULL || t == NULL)
    27         return -1;
    28 
    29     while (s[i] && (j == -1 || t[j]))
    30     {
    31         if (j == -1 || s[i] == t[j]) //j==-1说明s[i]开头的字串不可能和t匹配,所以s直接到下一个字符去,理解这个很重要  
    32         {
    33             i++;
    34             j++;
    35         }
    36         else 
    37             j = next[j];
    38     }
    39     if (!t[j])
    40         return (i - j);
    41     return -1;
    42 }

    next[i](假设next[i]=k)记录的是P[0~k]==P[i-1-k~i-1],这点和前面的理论稍有不同。

  • 相关阅读:
    java导出Excel表格
    移动端下拉刷新上拉加载-mescroll.js插件
    java-生成任意格式的json数据
    原生js版分页插件
    JavaScript实现段落文本高亮
    学习表单重复提交问题
    java 数据库连接 驱动相关参数
    mybatis maven 代码生成器(mysql)
    maven国内镜像
    spring boot redis代码与配置
  • 原文地址:https://www.cnblogs.com/chengxuyuancc/p/2867779.html
Copyright © 2011-2022 走看看