• kmp专题


     1 int nex[maxn];
     2 void get_nex(char str2[])
     3 {
     4     nex[0]=0;
     5     int len=strlen(str2);
     6     int i=0;
     7     for(int i=1; i<len; i++)
     8     {
     9         int j=nex[i-1];
    10         while(j&&str2[i]!=str2[j])
    11             j=nex[j-1];
    12         if(str2[i]==str2[j])
    13             nex[i]=j+1;
    14         else
    15             nex[i]=0;
    16     }
    17 }
    18 int kmp(char str1[],char str2[])
    19 {
    20     get_nex(str2);
    21     int len1=strlen(str1);
    22     int len2=strlen(str2);
    23     int i=0,j=0,ans=0;
    24     while(i<len1&&j<len2)
    25     {
    26         if(str1[i]==str2[j])
    27         {
    28             i++,j++;
    29         }
    30         else
    31         {
    32             if(!j)
    33                 i++;
    34             else
    35                 j=nex[j-1];
    36         }
    37         if(j==len2)
    38         {
    39             j=nex[j-1];
    40             ans++;
    41         }
    42     }
    43     return ans;
    44 }
    nex[0]=0 开头的kmp模板
    nex[0]=-1开头的kmp模板

    题目链接:https://cn.vjudge.net/contest/276379#problem/A

    A题:(kmp模板题)

    我的理解:首先nex数组的作用就是判断当前位置是否具有和使得前缀和与后缀和相等,如果存在的话,那么可以直接跳过前缀和,从前缀和的下一个开始匹配。

    AC代码:

     1 #include<iostream>
     2 #include<stdio.h>
     3 using namespace std;
     4 # define ll long long
     5 const int maxn = 1000000+100;
     6 int a[maxn],b[maxn];
     7 int nex[maxn];
     8 int n,m;
     9 void getnex()
    10 {
    11     int i=0,j=-1;
    12     nex[0]=-1;
    13     while(i<m-1)
    14     {
    15         if(j==-1||b[i]==b[j])
    16         {
    17             i++;
    18             j++;
    19             nex[i]=j;
    20         }
    21         else
    22         {
    23             j=nex[j];
    24         }
    25     }
    26 }
    27 int kmp()
    28 {
    29     int i=0,j=0;
    30     while(i<n&&j<m)
    31     {
    32         if(j==-1||a[i]==b[j])
    33         {
    34             i++;
    35             j++;
    36         }
    37         else
    38         {
    39             j=nex[j];
    40         }
    41     }
    42     if(j==m)
    43         return i-j;
    44     return -1;
    45 }
    46 int main()
    47 {
    48     int T;
    49     scanf("%d",&T);
    50     while(T--)
    51     {
    52         scanf("%d %d",&n,&m);
    53         for(int i=0; i<n; i++)
    54         {
    55             scanf("%d",&a[i]);
    56         }
    57         for(int i=0; i<m; i++)
    58         {
    59             scanf("%d",&b[i]);
    60         }
    61         getnex();
    62         int ans=kmp();
    63         if(ans!=-1)
    64             ans++;
    65         printf("%d
    ",ans);
    66     }
    67     return 0;
    68 }

    B题,s2中查找子串s1的个数。

    getnex是对s1进行操作的

    AC代码:

     1 #include<iostream>
     2 #include<stack>
     3 #include<cstring>
     4 #include<queue>
     5 #include<stdio.h>
     6 #include<map>
     7 #include<vector>
     8 #include<cmath>
     9 #include<string>
    10 using namespace std;
    11 # define ll long long
    12 const int maxn = 1000000+100;
    13 char str1[maxn],str2[maxn];
    14 int nex[maxn];
    15 void getnex(int t)
    16 {
    17     nex[0]=-1;
    18     int i=0,j=-1;
    19     while(i<t-1)
    20     {
    21         if(j==-1||str1[i]==str1[j])
    22         {
    23             i++;
    24             j++;
    25             nex[i]=j;
    26         }
    27         else
    28         {
    29             j=nex[j];
    30         }
    31     }
    32 }
    33 int kmp(int t1,int t2)
    34 {
    35     int i=0,j=0;
    36     int ans=0;
    37     while(j<t2)
    38     {
    39         if(i==-1||str1[i]==str2[j])
    40         {
    41             i++;
    42             j++;
    43         }
    44         else
    45         {
    46             i=nex[i];
    47         }
    48         if(i==t1)
    49             ans++,i=nex[i];//如果已经匹配完毕,回到头就可以了。
    50     }
    51     return ans;
    52 }
    53 int main()
    54 {
    55     int T;
    56     scanf("%d",&T);
    57     while(T--)
    58     {
    59         scanf("%s",str1);
    60         scanf("%s",str2);
    61         int len1=strlen(str1);
    62         int len2=strlen(str2);
    63         getnex(len2);
    64         int ans=kmp(len1,len2);
    65         printf("%d
    ",ans);
    66     }
    67     return 0;
    68 }
    69 /*
    70 3
    71 AZA
    72 AZAZAZA
    73 */

    C题:

    寻找最少加几个字母,能够使得新得到的字符串是由循环节构成的。

    具体思路:首先找到满足初始子串的循环节,然后再判断一下,原来字符串长度。

    如果能被循环节整除,如果循环节只存在一个,那么直接输出原来的长度就可以了,如果存在多个的话,输出0就可以了。

    另一种情况,看还剩下多少余下的,补足就可以了。

    AC代码:

     1 #include<iostream>
     2 #include<stack>
     3 #include<stdio.h>
     4 #include<queue>
     5 #include<string>
     6 #include<cstring>
     7 #include<cmath>
     8 using namespace std;
     9 # define ll long long
    10 const int maxn = 100000+100;
    11 char str[maxn];
    12 int nex[maxn];
    13 void getnex(int len)
    14 {
    15     nex[0]=-1;
    16     int i=-1,j=0;
    17     while(j<len)
    18     {
    19         if(i==-1||str[i]==str[j])
    20         {
    21             i++;
    22             j++;
    23             nex[j]=i;
    24         }
    25         else
    26         {
    27             i=nex[i];
    28         }
    29     }
    30 }
    31 int main()
    32 {
    33     int T;
    34     scanf("%d",&T);
    35     while(T--)
    36     {
    37         scanf("%s",str);
    38         int len=strlen(str);
    39         getnex(len);
    40         int tmp=len-nex[len];
    41         if(len%tmp==0)
    42         {
    43             if(len/tmp>=2)
    44                 printf("0
    ");
    45             else
    46             {
    47                 printf("%d
    ",len);
    48             }
    49         }
    50         else
    51         {
    52           //  if(len/tmp>=1)
    53                 printf("%d
    ",tmp-len%tmp);
    54 //            else
    55 //            {
    56 //                printf("%d
    ",len);
    57 //            }
    58 
    59         }
    60     }
    61     return 0;
    62 }

    D题:

    对于每一位,寻找当前这一位之前的循环节个数,如果不为零,则输出。

    具体思路:首先是循环节的寻找过程,对于当前这一位的循环节是len-nex[len],这里的len指的并不是总的长度,而是指的是从头开始,到这一位的长度。

    AC代码:

     1 #include<iostream>
     2 #include<stack>
     3 #include<iomanip>
     4 #include<stdio.h>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<string>
     8 #include<cstring>
     9 #include<vector>
    10 using namespace std;
    11 # define ll long long
    12 const int maxn = 1e6+10;
    13 char str[maxn];
    14 int nex[maxn];
    15 void getnex(int len)
    16 {
    17     nex[0]=-1;
    18     int i=0,j=-1;
    19     while(i<len)
    20     {
    21         if(j==-1||str[i]==str[j])
    22         {
    23             i++;
    24             j++;
    25             nex[i]=j;
    26         }
    27         else
    28         {
    29             j=nex[j];
    30         }
    31     }
    32 }
    33 int main()
    34 {
    35     int len;
    36     int Case=0;
    37     while(~scanf("%d",&len)&&len)
    38     {
    39         scanf("%s",str);
    40         getnex(len);
    41 //        for(int i=0;i<=len;i++){
    42 //        cout<<i<<" "<<nex[i]<<endl;
    43 //        }
    44         printf("Test case #%d
    ",++Case);
    45         for(int i=2; i<=len; i++)
    46         {
    47             int tmp=i-nex[i];
    48             if(tmp==0||nex[i]==0)//如果tmp为0或者nex【i】为0,代表当前这一位之前没有循环节
    49                 continue;
    50             if(i%tmp==0)//必须能够整除才能算上。
    51             {
    52                 printf("%d %d
    ",i,i/tmp);
    53             }
    54         }
    55         printf("
    ");
    56     }
    57     return 0;
    58 }
    59  
  • 相关阅读:
    常用语句
    html引入ECharts的两种方式
    最新版Navicate破解激活
    买路由器篇
    关于java8(Stream)的一些用法
    Mybatis联合查询记录,左连接参数操作
    海淘转运事宜记录
    mysql where语句多条件查询是and和or联合使用bug
    关于mysql中GROUP_CONCAT函数的使用
    关于MySQL存入的时间和取出时间不一致的解决
  • 原文地址:https://www.cnblogs.com/letlifestop/p/10262763.html
Copyright © 2020-2023  润新知