• 洛谷 P3375 【模板】KMP字符串匹配 || HDU 1686 Oulipo || kmp


    HDU-1686

    P3375

    kmp介绍:

    http://www.matrix67.com/blog/archives/115

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

    http://blog.chinaunix.net/uid-8735300-id-2017161.html(mp&kmp)

    http://www-igm.univ-mlv.fr/~lecroq/string/node8.html(mp&kmp,看上去很正确的例程)

    http://blog.csdn.net/joylnwang/article/details/6778316

    洛谷P3375:

    字符串下标从1开始:

     1 #include<cstdio>
     2 #include<cstring>
     3 char s1[1000010],s[1010];
     4 int f[1010];
     5 int n,m;
     6 void getf()
     7 {
     8     int i,j=0;
     9     f[0]=-1;f[1]=0;
    10     for(i=2;i<=m;i++)
    11     {
    12         //j=f[i-1]其实开始的时候j是这个值
    13         while(j>=0&&s[j+1]!=s[i])    j=f[j];
    14         j++;
    15         /*这一步后就是满足s[1..j]==s[i-j+1..i]
    16         显然如果找不到任何j那么j应该为0,而-1+1=0,所以f[0]=-1,
    17         否则应当为s[1..i-1]的某个相同的前缀后缀且它们之后都为s[i]*/
    18         f[i]=j;
    19     }
    20 }
    21 void kmp()
    22 {
    23     int ans=0,i,j=0;
    24     for(i=1;i<=n;i++)
    25     {
    26         //初始时j为s[1..j]==s[i-1-j+1..i-1]
    27         while(j>=0&&s[j+1]!=s1[i])    j=f[j];
    28         j++;
    29         //此时使得s往后移到了正确的位置
    30         /*例如ABABABC/ABC:i=3时j=1,也就是s开头1位跟s1[1..i]对齐
    31         又如ABDBA/ABC:i=3时j=0,也就是s开头0位跟s1[1..i]对齐*/
    32         if(j==m)
    33         {
    34             //ans++;
    35             printf("%d
    ",i-m+1);
    36             j=f[j];
    37         }
    38     }
    39     //return ans;
    40 }
    41 int main()
    42 {
    43     int i;
    44     scanf("%s%s",s1+1,s+1);
    45     n=strlen(s1+1);
    46     m=strlen(s+1);
    47     getf();
    48     kmp();
    49     for(i=1;i<=m;i++)
    50         printf("%d ",f[i]);
    51     return 0;
    52 }

    字符串下标从0开始,加上某个优化后开两个f:

     1 #include<cstdio>
     2 #include<cstring>
     3 int f[1010],f2[1010];
     4 char s1[1000100],s[1010];
     5 int n,m;
     6 void getf()
     7 {
     8     int i=0,j=f[0]=-1;
     9     while(i<m)
    10     {
    11         while(j>=0&&s[j]!=s[i])    j=f[j];
    12         ++i;++j;
    13         f[i]=s[i]==s[j]?f[j]:j;
    14         f2[i]=j;
    15     }
    16 }
    17 void kmp()
    18 {
    19     int i=0,j=0;
    20     while(i<n)
    21     {
    22         while(j>=0&&s[j]!=s1[i])    j=f[j];
    23         ++i;++j;
    24         if(j==m)    printf("%d
    ",i-m+1);
    25     }
    26 }
    27 int main()
    28 {
    29     int i;
    30     scanf("%s%s",s1,s);
    31     n=strlen(s1);
    32     m=strlen(s);
    33     getf();
    34     kmp();
    35     for(i=1;i<=m;i++)
    36         printf("%d ",f2[i]);
    37     return 0;
    38 }

    HDU-1686:

    程序1的f[i]的含义是能使s[0..p-1]==s[i-p..i-1]的最大的p。字符串下标从0开始。也就是如果当对齐原串下标i的是模板串下标j时发生失配,可以将对齐原串下标i的改为模板串下标f[j]。

    程序1(蓝书模板)(f[0]定义为0):

     1 #include<cstdio>
     2 #include<cstring>
     3 int f[10100];
     4 char s1[1000100],s[10100];
     5 void getFail(char *P,int *f)
     6 {
     7     int m=strlen(P);
     8     f[0]=0;f[1]=0;
     9     for(int i=1;i<m;i++)
    10     {
    11         int j=f[i];
    12         while(j&&P[i]!=P[j])    j=f[j];
    13         f[i+1]=P[i]==P[j]?j+1:0;
    14     }
    15 }
    16 int find(char *T,char *P,int *f)
    17 {
    18     int n=strlen(T),m=strlen(P),ans=0;
    19     getFail(P,f);
    20     int j=0;
    21     for(int i=0;i<n;i++)
    22     {
    23         while(j&&P[j]!=T[i])    j=f[j];
    24         if(P[j]==T[i])    j++;
    25         if(j==m)    ans++;
    26     }
    27     return ans;
    28 }
    29 int main()
    30 {
    31     int t;
    32     scanf("%d",&t);
    33     while(t--)
    34     {
    35         scanf("%s%s",s,s1);
    36         printf("%d
    ",find(s1,s,f));
    37     }
    38     return 0;
    39 }

    程序2(魔改蓝书&某个例程)(f[0]定义为-1,已经加了某个优化):

     1 #include<cstdio>
     2 #include<cstring>
     3 int f2[10100];
     4 char s1[1000100],s[10010];
     5 void getFail(char *P,int *f)
     6 {
     7     int m=strlen(P),j=f[0]=-1,i=0;//i初始值不能为1
     8     while(i<m)
     9     {
    10         while(j>=0&&P[i]!=P[j])    j=f[j];
    11         ++j;++i;
    12         f[i]=P[i]==P[j]?f[j]:j;//改为f[i]=j就是没有优化
    13     }
    14 }
    15 int find(char *T,char *P,int *f)
    16 {
    17     int n=strlen(T),m=strlen(P),ans=0;
    18     getFail(P,f);
    19     int i=0,j=0;
    20     while(i<n)
    21     {
    22         while(j>=0&&P[j]!=T[i])    j=f[j];
    23         ++i,++j;
    24         if(j==m)    ans++;//本来还有一句j=f[j],但这句可以去掉,因为此时j==m,P[j]=='',一定不等于T[i],一定执行j=f[j]
    25     }
    26     return ans;
    27 }
    28 int main()
    29 {
    30     int t;
    31     scanf("%d",&t);
    32     while(t--)
    33     {
    34         scanf("%s%s",s,s1);
    35         printf("%d
    ",find(s1,s,f2));
    36     }
    37     return 0;
    38 }

    为什么当s[i]!=s[j]时,j=f[j]:

    http://blog.csdn.net/qq_30974369/article/details/74276186

    http://blog.csdn.net/yutianzuijin/article/details/11954939/

    http://blog.csdn.net/wangbaochu/article/details/50687160

    (要求从next[i]一段的末尾找到一段k,使得k和(开头的next[i]开头的)一段相同。也就是在next[i]的一段中找出开头和结尾相等的一段。(根据第2、3个博客的图))

     

    将各个段从左到右编号为1(灰)、2(紫)、3(红)、4(灰)、5(绿)、6(黑)、7(灰)、8(红)、9(灰)、10(蓝)、11(黑)。

    现在的情况是要寻找1-10这大段的最长公共前后缀。用f[n]表示1-n这一段的最长公共前后缀(或其长度),s[n]表示n位置的值。

    如果s[5]==s[10],那么显然1-10的公共前后缀可以是f[9]的后面加上一个10。

    可以证明这就是1-10的最长公共前后缀:如果不是,也就是f[10]>f[9]+1,也就是f[10]-1>f[9],那么将f[10]删除尾部的s[10],剩下长度为f[10]-1,那么剩下的部分是1-9的公共前后缀,且其长度大于f[9],矛盾。

    如果s[5]!=s[10],那么就是要在1-9中找出1-x使得1-x一段是1-9的公共前后缀且s[x+1]==s[10]。

    先考虑第一个条件,再去满足第二个:那么也就是要找出1段和9段相同且不等于1-4段。由于1-4段和7-9段是1-9段的公共前后缀,那么1-4段==7-9段,也就是可以在7-9段中找到7段对应1段,在1-4段中找到4段对应9段。可以知道1、4、7、9段都相等。

    由于1、4段相等,1、4段也是1-4这一段的公共前后缀。因此可以直接找f[4],结果也就是1-9段的次长公共前后缀。如果还是不行,就找f[f[4]],f[f[f[4]]],...

  • 相关阅读:
    Docker 容器知识点
    Docker 常用命令
    杜教筛
    后缀自动机相关
    期望DP
    从零开始的莫比乌斯反演(函数)[详细推导]
    欧拉函数|(扩展)欧拉定理|欧拉反演
    优美诗词(持续更新)
    魔法 [线段树优化DP]
    stone2 [期望]
  • 原文地址:https://www.cnblogs.com/hehe54321/p/7530638.html
Copyright © 2020-2023  润新知