zoukankan      html  css  js  c++  java
  • KMP的初步认识及题目分析

    KMP算法对于像我这样的ACM菜鸡来说实在难以理解

    虽然有大佬讲课,但是还是不理解

    感谢下面这位大犇的博客,让我看懂了KMP(跪谢)

    https://www.cnblogs.com/SYCstudio/p/7194315.html

    ---------------------------------------------------------------------------------------------------------------------------

    下面是我对于学校题目的一些总结

    KMP模板(解释太麻烦了,看上面的大佬博客就行)

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string>
     4 using namespace std;
     5 int tnext[1000010];
     6 
     7 void get_next(string ptr)
     8 {
     9     tnext[0] = -1;
    10     int len = ptr.size();
    11     int j;
    12     for (int i = 1; i <len; i++)
    13     {
    14         int j = tnext[i - 1];
    15         while (j>=0&&ptr[i] != ptr[j+1])
    16         {
    17             j = tnext[j];
    18         }
    19         if (ptr[i] == ptr[j+1]) j++;
    20         tnext[i] = j;
    21        // cout << tnext[i] << endl;
    22     }
    23 
    24 }
    25 void KMP(string str, string ptr)
    26 {
    27     get_next(ptr);
    28     for (int i = 0, j = -1; i < str.size(); i++)
    29     {
    30         while (j >= 0 && (j == str.size() - 1 || str[i] != ptr[j+1]))
    31         {
    32             j = tnext[j];
    33         }
    34         if (str[i] == ptr[j + 1]) j++;
    35         if (j == ptr.size() - 1) cout << i - j << endl;
    36     }
    37 
    38 }
    39 int main()
    40 {
    41     string str, ptr;//str是长串,ptr是短串即目标串,要在str中去找ptr
    42     cin >> str >> ptr;
    43     KMP(str,ptr);
    44     return 0;
    45 }

    ---------------------------------------------------------------------------------------------------------------------------

    HDU-4763-Theme Section

    http://acm.hdu.edu.cn/showproblem.php?pid=4763

    本题大意就是要求找出一串字符串的border,且该border在中间出现过

    思路是先算next数组,如果next数组在全长的情况下不是-1,那么就进一步去字符串里找border

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string>
     4 #include <cstring>
     5 using namespace std;
     6 int tnext[1000010];
     7 
     8 void get_next(string ptr)
     9 {
    10     tnext[0] = -1;
    11     int len = ptr.size();
    12     int j;
    13     for (int i = 1; i < len; i++)
    14     {
    15         int j = tnext[i - 1];
    16         while (j >= 0 && ptr[i] != ptr[j + 1])
    17         {
    18             j = tnext[j];
    19         }
    20         if (ptr[i] == ptr[j + 1]) j++;
    21         tnext[i] = j;
    22         // cout << tnext[i] << endl;
    23     }
    24 
    25 }
    26 bool cc;
    27 void KMP(string str, string ptr)
    28 {
    29     get_next(ptr);
    30     for (int i = 0, j = -1; i < str.size(); i++)
    31     {
    32         while (j >= 0 && (j == str.size() - 1 || str[i] != ptr[j + 1]))
    33         {
    34             j = tnext[j];
    35         }
    36         if (str[i] == ptr[j + 1]) j++;
    37         if (j == ptr.size() - 1)
    38         {
    39             if (i - j != 0 && i - j != str.size() - ptr.size() + 1) cc = true;
    40         }
    41     }
    42 
    43 }
    44 int main()
    45 {
    46     int n;
    47     cin >> n;
    48     while (n--)
    49     {
    50         string str;
    51         cin >> str;
    52         cc = false;
    53         memset(tnext, 0, sizeof(tnext));
    54         get_next(str);
    55         int k = tnext[str.size() - 1];
    56         while (2 * (k + 1) + 1 > str.size()) k = tnext[k];//前缀和后缀不能接壤
    57         if (k == -1) cout << "0" << endl;
    58         else
    59         {
    60             while (k != -1)//先是从满足条件的最大border开始判断,不行的话用小一点的border,直到没有
    61             {
    62                 string ptr(str, 0, k + 1);//截取border
    63 
    64                 KMP(str, ptr);
    65                 if (cc) {//找到了满足中间出现border条件的
    66                     cout << k + 1 << endl; break;
    67                 }
    68                 else k = tnext[k];//没找到就看下一层border
    69             }
    70             if(!cc) cout << 0 << endl;
    71         }
    72     }
    73     return 0;
    74 
    75 }

    ---------------------------------------------------------------------------------------------------------------------------

    HDU-3336-Count the string

     http://acm.hdu.edu.cn/showproblem.php?pid=3336

    题目大意就是找出一个字符串的所有前缀在字符串里出现过几次

    思路是:由于每个前缀自身都会出现一次,如果一个字符串长度为n,那么总共有n个前缀,所以前缀至少出现n次

    之后任务就变成了前缀在其之后出现了几次

    而next数组的含义是字符串长度为k时,其border的长度,如果我们遍历next数组求和,就可以达到有多少个前缀出现在了后面

     1 #include <iostream>
     2 #include <cstring>
     3 #include<string>
     4 using namespace std;
     5 int pos[1000100];
     6 int num = 0;
     7 void get_next(string ptr)
     8 {
     9     pos[0] = -1;
    10     
    11     for (long long int i = 1,j=-1; i < ptr.size(); i++)
    12     {
    13         
    14         while (j >= 0 && ptr[i] != ptr[j + 1]) j = pos[j];
    15         if (ptr[i] == ptr[j + 1]) j++;
    16         pos[i] = j;
    17        
    18     }
    19 
    20 }
    21 
    22 int main()
    23 {
    24     ios::sync_with_stdio(false);
    25     cin.tie(0);
    26     cout.tie(0);
    27     long long int t;
    28     cin >> t;
    29     while (t--)
    30     {
    31         long long int size;
    32         string str;
    33         cin >> size;
    34         cin >> str;
    35         num = 0;
    36         get_next(str);
    37         for (int i = 1; i < size; i++)
    38         {
    39             if (pos[i] != -1) num++;
    40             num %= 10007;
    41         }
    42        
    43        cout << (num+size)%10007 << endl;
    44     }
    45 }

    ---------------------------------------------------------------------------------------------------------------------------

    HDU-1358-Period

    http://acm.hdu.edu.cn/showproblem.php?pid=1358

    题目大意:有一串字符串,找出他的长度为多少时,是由循环节组成的

    解题思路:遍历总长度,算出每个长度对应的next数组,那么周期就是len-next[k],如果长度%周期==0

    说明它是由整数个周期组成的,输出长度/周期即可

     1 #include <iostream>
     2 #include<string>
     3 using namespace std;
     4 int pos[1000100];
     5 void get_pos(string str)
     6 {
     7     pos[0] = -1;
     8     for (int i = 1; i < str.size(); i++)
     9     {
    10         int j = pos[i - 1];
    11         while (j >= 0 && str[i] != str[j + 1]) j = pos[j];
    12         if (str[i] == str[j + 1]) j++;
    13         pos[i] = j;
    14     }
    15 }
    16 int main()
    17 {
    18     int n,num=0;
    19     while (cin >> n && n)
    20     {
    21         num++;
    22         //if (num != 1) cout << endl;
    23         string str;
    24         cin >> str;
    25         
    26        
    27         cout << "Test case #" << num << endl;
    28         get_pos(str);
    29         for (int i = 2; i <= str.size(); i++)
    30         {
    31             if (pos[i-1]!=-1&&(i % (i - pos[i - 1] - 1) == 0))//存在border是前提
    32             {
    33                 cout << i << " " << i / (i - pos[i - 1] - 1) << endl;
    34             }
    35         }
    36         cout << endl;
    37     }
    38 }

    ---------------------------------------------------------------------------------------------------------------------------

    HDU-3746-Cyclic Nacklace

    http://acm.hdu.edu.cn/showproblem.php?pid=3746

    题目大意:给出一字符串,要求问要补多少个字符才能凑出一个循环节构成的字符串

    思路:算出next数组,然后找出周期,周期-(长度%周期)就是需要补充的数量

    考虑到已经是循环节构成的字符串要输出0,没有border的要输出自身长度

    (此题cin,memset好像不能用,题目有问题)

     1 #include <iostream>
     2 #include<string>
     3 #include<cstring>
     4 #include<stdio.h>
     5 using namespace std;
     6 int pos[100010];
     7 char str[100010];
     8 int main()
     9 {
    10 
    11     int n;
    12     scanf("%d", &n);
    13     getchar();
    14     while (n--)
    15     {
    16         gets(str);
    17         pos[0] = -1;
    18         int len = strlen(str);
    19         for (int i = 1; i < len; i++)
    20         {
    21             int j = pos[i - 1];
    22             while (j >= 0 && str[i] != str[j + 1]) j = pos[j];
    23             if (str[i] == str[j + 1])j++;
    24             pos[i] = j;
    25         }
    26 
    27         int cyl = len - (pos[len - 1] + 1);
    28         int t = (cyl - (len % cyl)) % cyl;
    29         if (t == 0 && len / cyl == 1) t += len;
    30         
    31         printf("%d
    ", t);
    32     }
    33 }

     

  • 相关阅读:
    使用awk根据多维度统计系统tps
    Java实现身份证号码校验
    Java分布式数据导出实践
    Java服务器端消息队列实战
    Java获取当前服务器IP实现
    Jvm dump介绍与使用(内存与线程)
    Java进行身份证格式强校验(准)
    Java性能监控之Instrumentation
    基于linux操作系统安装、使用memcached详解
    基于linux操作系统安装、使用redis详解
  • 原文地址:https://www.cnblogs.com/Knightero/p/12830054.html
Copyright © 2011-2022 走看看