• 学习KMP算法


    KMP算法的核心是提前根据模式串计算出来一张表,这张表中记录着什么情况下能够往后移的幅度大一些,以减少重复的匹配,理解步骤如下:

    首先,应知道什么是失配,就是模式串在和目标文本的遍历匹配过程中遇到了不匹配的字符。

    其次,失配无非两种情况:首字符失配,非首字符失配。

    首字符失配时,模式串后移一步,匹配后面的字符。

    非首字符失配时,这时有个有价值的信息,就是我们已经知道了失配的字符之前的那些没有失配的那些字符,那些字符有什么特点呢?怎么样才能帮助我们减少匹配次数呢?

    那些字符的特点是:是模式串的从头开始的一个子串!

    KMP算法的核心正是分析它的子串,从而减少匹配次数。

    这个特点看起来很简单,合理利用的话,可以大幅减少匹配次数。

    我们的目的是,对于这些匹配过的字符,即这个模式串的“子串”,我们尽量不想再去遍历他们了,这时我们可以分析这个子串,它无非这两种情况:

    情况一:子串的头不能覆盖到尾部,即子串的尾部是否有子串的子串,例如:

    abcde,对于它,我们可以放心的把模式串后移到e后面,直接跳过了子串长度个字符,因为这个子串从尾部往前覆盖无论如何也覆盖不上,要想首字符不失配,必须保证这个子串的从尾部开始的子串是它从头开始的子串,就像下面这种情况。

    情况二:子串的头可以覆盖到尾部:即子串从尾部开始,存在“小子串”是这个子串从头部开始的“小子串”,例如:

    abcdeab,对于他,尾部的ab正好是abcdeab的一个从头开始的“小子串”,这样才能满足非首字符失配的条件,这种情况下,将会根据“小子串”的长度去移动模式串,如果不满足这个条件,就是首字符失配,那样就直接把模式串的首字符放到指针位置去匹配就可以了,就像第一种情况那样。

    对于情况一和情况二,我们需要提前就计算出来模式串的那些子串头能覆盖到尾,即存在子串的“小子串”,如abcdeabfed是模式串的话,abcdeab就是一个子串,ab就是这个子串的“小子串”。计算出来的数据放到一张表里,根据这张表,我们就能跳过一些没必要的遍历,完成快速匹配了,

    最后,上代码:

      1 #include <stdlib.h>
      2 #include <string>
      3 #include <sys/timeb.h>
      4 #include <fstream>
      5 /**
      6 /* @author:zanzan101
      7 */
      8 
      9 using namespace std;
     10 
     11 // 计算每一个子串的K
     12 int aux(const char* str, int len)
     13 {
     14     if(len <= 1)
     15         return 0;
     16     bool b;
     17     for(int i = 1; i < len; i++)
     18     {
     19         b = true;
     20         for(int j = 0; j < len-i && b; j++)
     21             if(str[j] != str[i+j])
     22                 b = false;
     23         if(b)
     24             return len - i;                // 返回K,K=1表示能覆盖最后一个字符
     25     }
     26     return 0;                            // 返回0表示无法覆盖
     27 }
     28 
     29 // 计算所有子串的K
     30 int* cal_table(const char* pattern_str, int len)
     31 {
     32     int* table = new int[len];
     33     table[0] = -1;
     34     table[1] = 0;
     35     for(int i = 2; i < len; i++)        // 失配时,子串长度一定小于len,所以,不需要计算table[len]
     36         table[i] = aux(pattern_str, i);
     37     return table;
     38 }
     39 
     40 // 查找匹配位置,返回下标
     41 int find_string(const char* str, int len_str, const char* pattern_str, int len_pattern_str)
     42 {
     43     if(len_str < 1)
     44         return -1;
     45     int* table = cal_table(pattern_str, len_pattern_str);
     46     bool b = true;
     47     int i = 0, j = 0;
     48     while(i <= len_str - len_pattern_str)
     49     {
     50         j = 0;
     51         b = true;
     52         while(j < len_pattern_str && b)
     53         {
     54             if(str[i+j] != pattern_str[j])
     55             {
     56                 b = false;
     57                 i += j - table[j];    // 当在长度为j+1的子串处失配时,查找长度为j的子串的最大k,即table[j]
     58             }else
     59                 j++;
     60         }
     61         if(b)
     62         {
     63             delete table;
     64             return i;
     65         }
     66     }
     67     delete table;
     68     return -1;
     69 }
     70 // 查找匹配位置,返回下标
     71 int find_string_opt(const char* str, int len_str, const char* pattern_str, int len_pattern_str)
     72 {
     73     if(len_str < 1)
     74         return -1;
     75     int* table = cal_table(pattern_str, len_pattern_str);
     76     int i = 0, j = 0;
     77     while(i <= len_str - len_pattern_str)
     78     {
     79         while(j < len_pattern_str)
     80         {
     81             if(str[i+j] != pattern_str[j])
     82             {
     83                 i += j - table[j];            // 当在长度为j+1的子串处失配时,查找长度为j的子串的最大k,即table[j]
     84                 j = table[j]>0?table[j]:0;    // 这句代码如果改成 j = max(table[j], 0); 算法将会变慢
     85             }else
     86                 j++;
     87         }
     88 
     89         delete table;
     90         return i;
     91 
     92     }
     93     delete table;
     94     return -1;
     95 }
     96 
     97 
     98 // 查找匹配位置,显示匹配过程
     99 int find_string_for_fun(const char* str, int len_str, const char* pattern_str, int len_pattern_str)
    100 {
    101     if(len_str < 1)
    102         return -1;
    103     int* table = cal_table(pattern_str, len_pattern_str);
    104     bool b = true;
    105     for(int i = 0; i <= len_str - len_pattern_str;)
    106     {
    107         b = true;
    108         for(int j = 0; j < len_pattern_str && b; j++)
    109         {
    110             if(str[i+j] != pattern_str[j])
    111             {
    112                 for(int k = 0; k < i; k++)
    113                     printf(" ");
    114                 for(int m = 0; m < len_pattern_str; m++)
    115                     if(m != j)
    116                         printf("%c", pattern_str[m]);
    117                     else
    118                         printf("_");
    119                 printf("
    ");
    120                 b = false;
    121                 i += j - table[j];    // 当在长度为j+1的子串处失配时,查找长度为j的子串的最大k,即table[j]
    122             }
    123         }
    124         if(b)
    125         {
    126             for(int k = 0; k < i; k++)
    127                 printf(" ");
    128             for(int m = 0; m < len_pattern_str; m++)
    129                 printf("%c", pattern_str[m]);
    130             printf("
    ");
    131             delete table;
    132             return i;
    133         }
    134     }
    135     delete table;
    136     return -1;
    137 }
    138 
    139 // 查找匹配位置,显示匹配过程
    140 int find_string_for_fun_opt(const char* str, int len_str, const char* pattern_str, int len_pattern_str)
    141 {
    142     if(len_str < 1)
    143         return -1;
    144     int* table = cal_table(pattern_str, len_pattern_str);
    145 
    146     int i = 0, j = 0;
    147     while(i <= len_str - len_pattern_str)
    148     {
    149         while(j < len_pattern_str)
    150         {
    151             if(str[i+j] != pattern_str[j])
    152             {
    153                 for(int k = 0; k < i; k++)
    154                     printf(" ");
    155                 for(int m = 0; m < len_pattern_str; m++)
    156                     if(m != j)
    157                         printf("%c", pattern_str[m]);
    158                     else
    159                         printf("_");
    160                 printf("
    ");
    161                 i += j - table[j];        // 当在长度为j+1的子串处失配时,查找长度为j的子串的最大k,即table[j]
    162                 j = table[j]>0?table[j]:0;    // 这里做了一点点优化,比上面的函数可以少匹配一些
    163             }else
    164                 j++;
    165         }
    166 
    167         for(int k = 0; k < i; k++)
    168             printf(" ");
    169         for(int m = 0; m < len_pattern_str; m++)
    170             printf("%c", pattern_str[m]);
    171         printf("
    ");
    172         delete table;
    173         return i;
    174 
    175     }
    176     delete table;
    177     return -1;
    178 }
    179 
    180 int main()
    181 {
    182     timeb time_begin;
    183     timeb time_end;
    184     unsigned int seconds;
    185     unsigned int miseconds;
    186     //////////////////////////////////////////////////////////////////////////
    187     // 短文本匹配
    188     const char* long_str = "adadaaddabadaadadaadadaadadaadaaddad";
    189     const char* short_str = "adaaddad";
    190     printf("%s
    ", long_str);
    191     printf("%s
    ", short_str);
    192     ftime(&time_begin);    
    193     int idx_fun = find_string_for_fun(long_str, strlen(long_str), short_str, strlen(short_str));
    194     ftime(&time_end);
    195     printf("_ 表示失配的位置
    
    ");
    196     seconds = time_end.time - time_begin.time;
    197     miseconds = time_end.millitm - time_begin.millitm;
    198     miseconds = seconds * 1000 + miseconds;
    199     printf("KMP方法:	处理时间为:	%u	毫秒
    ", miseconds);
    200 
    201     printf("%s
    ", long_str);
    202     printf("%s
    ", short_str);
    203     ftime(&time_begin);    
    204     idx_fun = find_string_for_fun_opt(long_str, strlen(long_str), short_str, strlen(short_str));
    205     ftime(&time_end);
    206     printf("_ 表示失配的位置
    ");
    207     seconds = time_end.time - time_begin.time;
    208     miseconds = time_end.millitm - time_begin.millitm;
    209     miseconds = seconds * 1000 + miseconds;
    210     printf("KMP_2方法:	处理时间为:	%u	毫秒
    ", miseconds);
    211     printf(">> result is :
    %s
    
    ", long_str+idx_fun);
    212 
    213 
    214     //////////////////////////////////////////////////////////////////////////
    215     // 处理一大批数据,和普通方法对比
    216     char* buff = new char[10240000]; // 10000KB的缓冲区
    217     ifstream input_file;
    218     int idx = -1;
    219     input_file.open("D:\input.txt");
    220     const char* str = "Uncle Vernon was waiting beyond the barrier. Mrs. Weasley wasclose by him. She hugged Harry very tightly when she saw him andwhispered in his ear, "I think Dumbledore will let you come to uslater in the summer. Keep in touch, Harry."";
    221     bool b;
    222 
    223 
    224     //////////////////////////////////////////////////////////////////////////
    225     // KMP方法
    226     ftime(&time_begin);    
    227     do{
    228         b = input_file.read(buff, 10240000);
    229         idx = find_string(buff, input_file.gcount(), str, strlen(str));
    230         if(idx >= 0)
    231             break;
    232     }while(b);
    233     ftime(&time_end);
    234     seconds = time_end.time - time_begin.time;
    235     miseconds = time_end.millitm - time_begin.millitm;
    236     miseconds = seconds * 1000 + miseconds;
    237     printf("KMP方法:	处理时间为:	%u	毫秒
    ", miseconds);
    238 
    239     input_file.close();
    240 
    241     input_file.open("D:\input.txt");
    242     //////////////////////////////////////////////////////////////////////////
    243     // KMP_2方法
    244     ftime(&time_begin);    
    245     do{
    246         b = input_file.read(buff, 10240000);
    247         idx = find_string_opt(buff, input_file.gcount(), str, strlen(str));
    248         if(idx >= 0)
    249             break;
    250     }while(b);
    251     ftime(&time_end);
    252     seconds = time_end.time - time_begin.time;
    253     miseconds = time_end.millitm - time_begin.millitm;
    254     miseconds = seconds * 1000 + miseconds;
    255     printf("KMP_2方法:	处理时间为:	%u	毫秒
    ", miseconds);
    256 
    257     input_file.close();
    258     input_file.open("D:\input.txt");
    259     bool f = false;
    260     int len = strlen(str);
    261 
    262     //////////////////////////////////////////////////////////////////////////
    263     // 朴素的方法
    264     ftime(&time_begin);    
    265     do{
    266         b = input_file.read(buff, 10240000);
    267         f = true;
    268         for(int i = 0; i <= input_file.gcount()-len; i++)
    269         {
    270             for(int j = 0; j < len; j++)
    271                 if(buff[i+j] != str[j])
    272                     f = false;
    273             if(f)
    274             {
    275                 idx = i;
    276                 break;
    277             }
    278         }
    279     }while(b&!f);
    280     ftime(&time_end);
    281     seconds = time_end.time - time_begin.time;
    282     miseconds = time_end.millitm - time_begin.millitm;
    283     miseconds = seconds * 1000 + miseconds;
    284     printf("朴素的方法:	处理时间为:	%u	毫秒
    ", miseconds);
    285     
    286     input_file.close();
    287     if(idx >= 0)
    288     {
    289         char* res = new char[strlen(str)];
    290         memcpy(res, buff+idx, strlen(str));
    291         res[strlen(str)] = 0;
    292         printf("
    >> result is : 
    %s
    ", res);
    293     }
    294 
    295     delete[] buff;
    296 
    297     system("pause");
    298     return 0;
    299 }

    输出结果:

    adadaaddabadaadadaadadaadadaadaaddad
    adaaddad
    ada_ddad
      adaadda_
            a_aaddad
             _daaddad
              adaad_ad
                 ada_ddad
                   adaad_ad
                      ada_ddad
                        adaad_ad
                           ada_ddad
                             adaad_ad
                                adaaddad
    _ 表示失配的位置
    
    KMP方法:       处理时间为:    15      毫秒
    adadaaddabadaadadaadadaadadaadaaddad
    adaaddad
    ada_ddad
      adaadda_
            a_aaddad
             _daaddad
              adaad_ad
                 ada_ddad
                   adaad_ad
                      ada_ddad
                        adaad_ad
                           ada_ddad
                             adaad_ad
                                adaaddad
    _ 表示失配的位置
    KMP_2方法:     处理时间为:    16      毫秒
    >> result is :
    adaaddad
    
    KMP方法:       处理时间为:    46      毫秒
    KMP_2方法:     处理时间为:    47      毫秒
    朴素的方法:    处理时间为:    3438    毫秒
    
    >> result is :
    Uncle Vernon was waiting beyond the barrier. Mrs. Weasley wasclose by him. She h
    ugged Harry very tightly when she saw him andwhispered in his ear, "I think Dumb
    ledore will let you come to uslater in the summer. Keep in touch, Harry."
    请按任意键继续. . .
  • 相关阅读:
    Response.Status http协议状态代码
    ASP.NET MVC 如何实现头压缩
    Google PR值原理和详细解说
    NodeJS 深入浅出
    C#: ToString格式
    HttpHandler实现媒体文件和图像文件的盗链(防盗链设计)
    ASP.NET MVC 使用Areas功能的常见错误
    VC中利用多线程技术实现线程之间的通信
    基于Visual C++的Winsock API研究
    键盘钩子程序
  • 原文地址:https://www.cnblogs.com/zanzan101/p/3346591.html
Copyright © 2020-2023  润新知