zoukankan      html  css  js  c++  java
  • KMP算法实现及应用

    1、KMP算法实现问题:

    KMP算法实现就是字符查找问题,假设现在有这样一个问题,有一个文本串S和一个模式串P,要查找P在S中的位置,即从文本串S中找出模式串P第一次出现的位置。

    问题分析:

    假设文本串长度为n,模式串长度为m。

    (1)暴力求解算法下,当两者匹配S[i] = P[j] 时,i++,j++;当不匹配时,i++,j=0。也就是说每次匹配失败时,模式串相对于文本串向右移动了一位。

    时间复杂度为O(m*n),空间复杂度为O(1)。

    (2)KMP算法下,当两者匹配S[i] = P[j] 时,i++,j++;当不匹配时,j = next[j](next[j] <= j-1)。也就是说每次匹配失败时,模式串相对于文本串向右至少移动一位,移动位数为j - next[j] >= 1。时间复杂度为O(m+n),空间复杂度为O(m)。

    在实现KMP算法之前,必须获取模式串P的next数组,设next[j]=k,即模式串前j-1位有k前缀和k后缀相等。

    获取模式串next数组程序实现:

     1 void GetNext(char* p,int next[]){
     2     int plen = strlen(p);
     3     int k = -1;
     4     next[0] = -1;
     5     int j;
     6     //此时,k表示next[j-1],p[j]表示后缀,p[k]表示前缀
     7     //k=-1表示未找到前缀后缀相等的情况,首次可先忽略
     8     while(j < plen -1){
     9         if(k ==-1 || p[j] == p[k]){
    10             k++;
    11             j++;
    12             next[j] = k;
    13         }
    14         else{
    15             k = next[k];
    16         }
    17     }
    18 }

    运行结果: 对于模式串 abaabcabc的next数组为:

    这里对next数组进行了改进,在原始的next数组中,当next[j] = k, p[j] = p[k]时,next[j] 可以直接等于next[k]。这样做的好处就是在KMP最差情况下,即模式串首字符与其他字符都相等的时候,时间复杂度能够提高,但还是在一个数量级。

    改进next数组代码实现:

     1 void GetNext1(char* p,int next[]){
     2     int plen = strlen(p);
     3     int k = -1;
     4     next[0] = -1;
     5     int j;
     6     //此时,k表示next[j-1],p[j]表示后缀,p[k]表示前缀
     7     //k=-1表示未找到前缀后缀相等的情况,首次可先忽略
     8     while(j < plen -1){
     9         if(k ==-1 || p[j] == p[k]){
    10             k++;
    11             j++;
    12             if(p[j] == p[k])//当前缀与后缀再次相等时,直接替换成next[k]
    13                 next[j] = next[k];
    14             else
    15                 next[j] = k;
    16         }
    17         else{
    18             k = next[k];
    19         }
    20     }
    21 }

    运行结果:改进后next数组为:

    在获取模式串next数组后,就可以进行KMP算法,进行字符串的查找。

    程序实现如下:

      1 /***************************************
      2 FileName KMPCode.cpp
      3 Author : godfrey
      4 CreatedTime : 2016/5/8
      5 ****************************************/
      6 #include <iostream>
      7 #include <cstring>
      8 #include <vector>
      9 #include <algorithm>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 
     13 using namespace std;
     14 
     15 void GetNext(char* p,int next[]){
     16     int plen = strlen(p);
     17     int k = -1;
     18     next[0] = -1;
     19     int j;
     20     //此时,k表示next[j-1],p[j]表示后缀,p[k]表示前缀
     21     //k=-1表示未找到前缀后缀相等的情况,首次可先忽略
     22     while(j < plen -1){
     23         if(k ==-1 || p[j] == p[k]){
     24             k++;
     25             j++;
     26             next[j] = k;
     27         }
     28         else{
     29             k = next[k];
     30         }
     31     }
     32 }
     33 
     34 void GetNext1(char* p,int next[]){
     35     int plen = strlen(p);
     36     int k = -1;
     37     next[0] = -1;
     38     int j;
     39     //此时,k表示next[j-1],p[j]表示后缀,p[k]表示前缀
     40     //k=-1表示未找到前缀后缀相等的情况,首次可先忽略
     41     while(j < plen -1){
     42         if(k ==-1 || p[j] == p[k]){
     43             k++;
     44             j++;
     45             if(p[j] == p[k])//当前缀与后缀再次相等时,直接替换成next[k]
     46                 next[j] = next[k];
     47             else
     48                 next[j] = k;
     49         }
     50         else{
     51             k = next[k];
     52         }
     53     }
     54 }
     55 
     56 int KMPCode(char* s,char* p,int p_next[]){
     57     int result = -1;
     58     int plen = strlen(p);
     59     int slen = strlen(s);
     60     int i=0;
     61     int j=0;
     62     while(i < slen){
     63         if(j == -1 || s[i] == p[j]){
     64             i++;
     65             j++;
     66         }
     67         else{
     68             j = p_next[j];
     69         }
     70 
     71         if(j == plen){
     72             result = i - plen;
     73             break;
     74         }
     75     }
     76     return result;
     77 }
     78 int main()
     79 {
     80     char s[1024],p[1024];
     81     int next[1025];
     82     int num;
     83     while(cin>>s>>p){
     84         //GetNext(p,next);
     85         GetNext1(p,next);
     86         int plen = strlen(p);
     87         cout<<"p[i] "<<"next[i] "<<endl;
     88         for(int i=0;i<plen;i++){
     89             cout<<p[i]<<"    "<<next[i]<<endl;
     90         }
     91         cout<<endl;
     92         num = KMPCode(s,p,next);
     93         if(num == -1){
     94             cout<<"Not found "<< p <<" in " << s <<endl;
     95         }
     96         else{
     97             cout<< "Found " << p << " in " << s << " at " <<num<<endl;
     98         }
     99     }
    100     return 0;
    101 }

    运行结果:

    2、KMP算法应用问题

    给定一个长度为n的字符串S,如果存在一个字符串P,重复若干次后P能够得到S,那么S叫做周期串,P叫做S的一个周期。

    如:字符串abcabcabc为周期串,abc,abcabc是它的周期,其中abc是最小周期。设计算法,计算S的最小周期大小,如果不存在最小周期,返回-1。

    问题分析:

    计算S的next数组,这里求解的是原始的next数组。记last = next[n-1], p = n - 1 -last;如果n%p = 0,则p就是最小周期长度,前p个字符就是最小周期。自己可以画图理解一下,还是比较好理解的。

    程序实现:

     1 /***************************************
     2 FileName StringMinPeriod.cpp
     3 Author : godfrey
     4 CreatedTime : 2016/5/8
     5 ****************************************/
     6 #include <iostream>
     7 #include <cstring>
     8 #include <vector>
     9 #include <algorithm>
    10 #include <stdio.h>
    11 #include <stdlib.h>
    12 
    13 using namespace std;
    14 
    15 int StringMinPeriod(char* p){
    16     int plen = strlen(p);
    17     if(plen == 0)
    18         return -1;
    19     int* next = new int[plen];
    20     int k = -1;
    21     next[0] = -1;
    22     int j = 0;
    23     while(j < plen -1){
    24         if((k ==-1) || (p[j] == p[k])){
    25             k++;
    26             j++;
    27             next[j] = k;
    28         }
    29         else{
    30             k = next[k];
    31         }
    32     }
    33 
    34     next[0] = 0;//将首位恢复为逻辑上的零
    35     int last = next[plen-1];
    36     if(last == 0)
    37         return -1;
    38     if(plen % (plen - last - 1) == 0)
    39         return plen - last - 1;
    40     delete[] next;
    41     return -1;
    42 }
    43 
    44 int main()
    45 {
    46     char s[1024];
    47     int MinPeriod;
    48     while(cin>>s){
    49         MinPeriod = StringMinPeriod(s);
    50         cout<<"the string MinPeriod : " <<MinPeriod<<"   ";
    51         for(int i=0;i<MinPeriod;i++)
    52             cout<<s[i];
    53         cout<<endl;
    54     }
    55     return 0;
    56 }

    运行结果:

    转载请注明出处:

    C++博客园:godfrey_88

    http://www.cnblogs.com/gaobaoru-articles/

    转载请注明出处: C++博客园:godfrey_88 http://www.cnblogs.com/gaobaoru-articles/
  • 相关阅读:
    Docker创建tomcat镜像简单使用
    Eclipse和Jdk版本问题
    HashTable源码阅读
    HashMap源码阅读
    报错:Multiple annotations found at this line:
    python全栈开发学习03
    python全栈开发学习 02
    关于termux在手机上搭载Linux系统,python,ssh
    python全栈开发学习 01
    机器学习实战
  • 原文地址:https://www.cnblogs.com/gaobaoru-articles/p/5471138.html
Copyright © 2011-2022 走看看