kmp算法(字符串匹配 next应用 hdu题目集合)

关于kmp算法的有关解释百度中有很多,可以自己查询更详细部分,其实最主要的就是next数组(以下题目我用的是on,因为hdu中直接用next变量或编译出错),当无法匹配时匹配字串跳转到前串中类似的位置(即是从无法匹配的前一个字符结束的类似字串)


题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=1686

题意:最简单的kmp应用,模板题,就是给你两个字符串,问你第二串种包含多少个第一个串,可以重叠,只要起始和结束位置不完全一样

题解:kmp算法

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>

using namespace std;
int nexton[10050];
char sons[10050];
char fathers[1000500];
int sum;
void getnext(char *c)
{
    int len=strlen(c);
    int j=-1,i=0;
    nexton[i]=j;
    while(i<=len)
    {
        if(j==-1||c[i]==c[j])
        {
            i++;j++;nexton[i]=j;
        }
        else
        {
            j=nexton[j];
        }
    }
}
int kmp(char *son,char *father)
{
    int slen=strlen(son),flen=strlen(father);
    int i=0,j=0;
    while(i<slen&&j<flen)
    {
        if(i==-1||son[i]==father[j])
        {
            i++;j++;
        }
        else
        {
            i=nexton[i];
        }
        if(i==slen)
        {
            i=nexton[i];
            sum++;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        sum=0;
        scanf("%s %s",sons,fathers);
        getnext(sons);
        kmp(sons,fathers);
        printf("%d\n",sum);
    }
}


题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=2087

题意:给你两个字符串,找出第一个字符串中包含多少个第二个字符串,不能重叠

题解:kmp算法,区别在于注释部分

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>

using namespace std;
char a[1005];
char b[1005];
int Next[1006];
int sum;
void getNext()
{
    int i=0,j=-1;
    Next[0]=j;
    int len=strlen(a);
    while(i<=len)
    {
        if(j==-1||a[j]==a[i])
        {
            Next[++i]=++j;
        }
        else
        {
            j=Next[j];
        }
    }
    /*for(int i0=0;i0<=len;i0++)
    {
        cout<<next[i0]<<"   ";
    }
    cout<<endl;*/

}
void kmp()
{
    int lena=strlen(a);
    int lenb=strlen(b);
    int i=0,j=0;
    while(i<=lena&&j<=lenb)
    {
        if(j==-1||a[i]==b[j])
        {
            i++;j++;
        }
        else
        {
            j=Next[j];
        }
        if(j==lenb)//当完全匹配时j要求置零,即是重新开始找
        {
            j=0;
            sum++;
        }
    }
}
int main()
{
    while(~scanf("%s",a)&&a[0]!='#')
    {
        scanf("%s",b);
        getNext();
        sum=0;
        kmp();
        printf("%d\n",sum);
    }
    return 0;
}


题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=1867

题意:给你两个字符串,让你把他们合成一个字符串,并且要求字符串中要包含他们(连续),即是若一个字符串前缀和另一个字符串的后缀相同,则把后缀的那个字符串放到前面就可省略另一个字符串的前缀,并且要求保证长度最短,字典序最小

题解:kmp算法,进行两次模式匹配,最后返回的就是第一个字符串的后缀和第二个匹配字符串的前缀的最大匹配,从而进行判断两次哪次最大,选择最大的。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define N 100050

using namespace std;
char a[N];
char b[N];
int on[N];
void  getNext(char *c,int len)
{
    int i=0,j=-1;
    on[0]=-1;
    while(i<=len)
    {
        if(j==-1||c[i]==c[j])
        {
            i++;j++;on[i]=j;
        }
        else
        {
            j=on[j];
        }
    }
}
int kmp(char *c,int lenc,char *p,int lenp)
{
    getNext(p,lenp);
    int i=0,j=0;
    while(i<lenc)
    {
        if(j==-1||c[i]==p[j])
        {
            i++;j++;
        }
        else
        {
            j=on[j];
        }
    }
    return j==-1?0:j;
}
int main()
{
    while(~scanf("%s %s",a,b))
    {
        int lena=strlen(a),lenb=strlen(b);
        int da=kmp(b,lenb,a,lena);
        int db=kmp(a,lena,b,lenb);
        if(da<db)
        {
            a[lena-db]='\0';
            printf("%s%s\n",a,b);
        }
        else if(da>db)
        {
            b[lenb-da]='\0';
            printf("%s%s\n",b,a);
        }
        else if(da==0)
        {
            int d=strcmp(a,b);
            if(d>=0)
            {
                b[lenb-db]='\0';
                printf("%s%s\n",b,a);
            }
            else
            {
                a[lena-da]='\0';
                printf("%s%s\n",a,b);
            }
        }
        else
        {
            char t1;
            t1 = a[lena-da];
            a[lena-da] = '\0';
            //printf("%s\n",a);
            string str1(a);
            str1 += b;
            a[lena-da] = t1;
            b[lenb-db] = '\0';
            //printf("%s\n",b);
            string str2(b);
            str2 += a;
            printf("%s\n",min(str1,str2).c_str());
        }
    }
    return 0;
}


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

题意:给你字符串,求从哪个字符为止前面有循环节,若有则输出该字符所在的位置和循环节个数

题解:先求出nexton数组,根据nexton数组(就是kmp中的next数组)的含义求循环节,若满足len%(len-next[i+1])==0&&len!=len-next[i+1]则必存在循环节,并且循环节个数为len/(len-next[i+1])

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>

using namespace std;
int nexton[1000005];
char fathers[1000005];
void getnext(char *c,int len)
{
    int j=-1,i=0;
    nexton[i]=j;
    while(i<=len)
    {
        if(j==-1||c[i]==c[j])
        {
            i++;j++;nexton[i]=j;
        }
        else
        {
            j=nexton[j];
        }
    }
}
int main()
{
    int t;
    int n=1;
    while(scanf("%d",&t)!=EOF&&t)
    {
        scanf("%s",fathers);
        getnext(fathers,t);
        printf("Test case #%d\n",n++);
        for(int i=2;i<=t;i++)
        {
            int d=i-nexton[i];
            if(i%d==0&&nexton[i]!=0)
            {
                printf("%d %d\n",i,i/d);
            }
        }
        printf("\n");
    }
    return 0;
}


题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=2594

题意:给你两个字符串,求一个字符串的前缀和另一个字符串的后缀匹配的最大长度,输出匹配的前缀或后缀

题解:与上面的一道题类似,题解见上

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define N 50000+5

using namespace std;
int nexton[N];
char a[N];
char b[N];
void getnext(char *c,int len)
{
    int i=0;int j=-1;
    nexton[i]=j;
    while(i<=len)
    {
        if(j==-1||c[i]==c[j])
        {
            i++;j++;nexton[i]=j;
        }
        else
        {
            j=nexton[j];
        }
    }
}
int kmp(char *s,int slen,char *f,int flen)
{
    int i=0;int j=0;
    getnext(s,slen);
    while(j<flen)
    {
        if(i==-1||s[i]==f[j])
        {
            i++;j++;
        }
        else
        {
            i=nexton[i];
        }
    }
    return i==-1?0:i;
}
int main()
{
    while(scanf("%s %s",a,b)!=EOF)
    {
        int alen=strlen(a);
        int blen=strlen(b);
        int da=kmp(a,alen,b,blen);
        if(da==0)
        {
            printf("0\n");
        }
        else
        {
            a[da]='\0';
            printf("%s %d\n",a,da);
        }
    }
    return 0;
}


题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=1711

题意:给定两个数字数组,要求第二个数字数组在第一个数组中匹配的起始点,输出最小的起始点

题解:简单的kmp应用,返回第一个匹配即可

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
 #define N 1000050

using namespace std;
int on[N];
int father[N];
int son[N];
void getnext(int *s,int slen)
{
    int i=1,j=0;
    on[1]=j;
    while(i<=slen)
    {
        if(j==0||s[i]==s[j])
        {
            i++;j++;on[i]=j;
        }
        else
        {
            j=on[j];
        }
    }
}
int kmp(int *s,int slen,int *f,int flen)
{
    int i=1,j=1;
    while(j<=flen)
    {
        if(i==0||s[i]==f[j])
        {
            i++;j++;
        }
        else
        {
            i=on[i];
        }
        if(i==slen+1)
        {
            return j-i+1;
        }
    }
    return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&father[i]);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&son[i]);
        }
        getnext(son,m);
        int d=kmp(son,m,father,n);
        if(!d){printf("-1\n");}
        else{printf("%d\n",d);}
    }
    return 0;
}


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

题意:给你一个字符串,求最小添加多少个字符就可完成循环

题解:先求出next数组,通过len%(len-next[len])==0&&len!=len-next[len]判断1-i是否已经构成不止一个的循环节,则直接输出0,不用再添加,若不满足,要通过(len-next[len])-len%(len-next[len])来求出要补充的循环节

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define N 100050

using namespace std;
int on[N];
char son[N];
void getNext(int slen)
{
    int i=0,j=-1;
    on[i]=j;
    while(i<=slen)
    {
        if(j==-1||son[i]==son[j])
        {
            i++;j++;on[i]=j;
        }
        else
        {
            j=on[j];
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",son);
        int len=strlen(son);
        getNext(len);
        if(len%(len-on[len])==0&&len!=(len-on[len]))
        {
            printf("0\n");
        }
        else
        {
            printf("%d\n",(len-on[len])-len%(len-on[len]));
        }
    }
    return 0;
}


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

题意:统计前缀值,包括与前缀相同但不再前缀而在字符串中的数量

题解:利用kmp中的next数组一个个进行判断,next数组就是相对于前缀求出的,方便统计数量

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define N  200050
using namespace std;
int  on[N];
char son[N];
void getnext(int len)
{
    int i=0,j=-1;
    on[i]=j;
    while(i<=len)
    {
        if(j==-1||son[i]==son[j])
        {
            j++;i++;on[i]=j;
        }
        else
        {
            j=on[j];
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",son);
        getnext(n);
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            int j=on[i];
            sum=(sum+1)%10007;
            while(j)
            {
                sum=(sum+1)%10007;
                j=on[j];
            }
        }
        printf("%d\n",sum);
    }
}


题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=1841

题意:给你两个字符串,要求合成一个字符串(包含两个字符串的内容)

题解:简单kmp应用

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#define N  1000050

using namespace std;
int on[N];
char a[N];
char b[N];

void getnext(char *c,int len)
{
    int i=0,j=-1;
    on[i]=j;
    while(i<=len)
    {
        if(j==-1||c[i]==c[j])
        {
            i++;j++;on[i]=j;
        }
        else
        {
            j=on[j];
        }
    }
}
int kmp(char *s,int slen,char *f,int flen)
{
     getnext(s,slen);
     int i=0,j=0;
     while(j<slen&&i<flen)
     {
         if(j==-1||s[j]==f[i])
         {
             i++;j++;
         }
         else
         {
             j=on[j];
         }
     }
     return j==-1?0:j;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s %s",a,b);
        int lena=strlen(a);
        int lenb=strlen(b);
        int da=kmp(a,lena,b,lenb);
        int db=kmp(b,lenb,a,lena);
        printf("%d\n",lena+lenb-max(da,db));
    }
    return 0;
}

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=3613

题意:给定一个字符串和一个26个价值的数组(代表26字母对应的价值),要求分成两段,其中如果一段满足回文,价值为价值和,否则为0

题解:求正反字符串的匹配,即求最长后缀的回文串和最长前缀的回文串,两者统一进行考虑

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#define N  500050

using namespace std;
int on[N];
char a[N];
char b[N];
int zi[28];
int first[N];//记录前缀的回文串标记
int last[N];//记录后缀的回文串标记
int sum[N];//记录左边有i个时的价值

void getnext(char *c,int len)
{
    int i=0,j=-1;
    on[i]=j;
    while(i<=len)
    {
        if(j==-1||c[i]==c[j])
        {
            i++;j++;on[i]=j;
        }
        else
        {
            j=on[j];
        }
    }
}
int kmp(char *s,int slen,char *f,int flen)
{
     getnext(s,slen);
     int i=0,j=0;
     while(j<slen&&i<flen)
     {
         if(j==-1||s[j]==f[i])
         {
             i++;j++;
         }
         else
         {
             j=on[j];
         }
     }
     return j;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        for(int i=0;i<26;i++) scanf("%d",&zi[i]);
        scanf("%s",a);
        int lena=strlen(a);
        sum[0]=0;
        for(int i=1;i<=lena;i++){b[lena-i]=a[i-1];sum[i]=sum[i-1]+zi[a[i-1]-'a'];}
        int da=kmp(a,lena,b,lena);//最大前串
        while(da){first[da]=t+1;da=on[da];}
        int db=kmp(b,lena,a,lena);
        while(db){last[db]=t+1;db=on[db];}
        int sum2=0;
        int ans=0;
        for(int i=1;i<lena;i++)
        {
            if(first[i]==t+1)sum2+=sum[i];
            if(last[lena-i]==t+1)sum2+=sum[lena]-sum[i];
            if(sum2>ans)ans=sum2;
            sum2=0;
        }
        printf("%d\n",ans);
    }
    return 0;
}



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值