hdu2087-自己的理解&kmp讲解-剪花布条

http://acm.hdu.edu.cn/showproblem.php?pid=2087
链接
时隔多年,终于理解kmp了。。
1 进行字符串匹配,如果进行暴力匹配的话,(即一个一个找,时间复杂度O(len1*len2),是不可取的,)
2 所以需要改进这个算法。 如何改进,这个问题,引起了三个年轻小伙子的注意,有一天他们在一个小溪边玩,突然发现了一个老太太在磨针,并且针的 头和尾各有一部分相同的花纹,就好像金箍棒的两头一样。然后,三个人之间的小k突发奇想,在纸上画了这么一个图。
这里写图片描述
他发现,大人们在进行暴力匹配的时候,当出现了蓝色的情况(红色是目标串,下面是模式串,如果前面的绿色相同,那么模式串就可以跳过去,跳过前面的比较。)
小m看了看,朗声道,想法不错,咋写啊。那个绿色的怎么表示??
小p说,好办,你有没有发现,这就是以i为最后一个字符 的 模式串的子串 的 最长的 相同前缀和后缀()??上面那个是后缀,虾米那那个是 前缀,(后缀顶到后,前缀要顶到前,实在不会,就先去学习 后缀数组qwq)
小m又说,如果转移又匹配失败了呢。。还要从头开始?
这时,小p说,一看你c语言课就没好好听,这不是老师常说的递归的思想么,如果匹配失败,就让 这个 最长的 相同前缀和后缀 当成一个 子串,继续递归寻找他的 ,判断 他的相同前缀和后缀 后面的那个元素是否和 蓝色字母相同。如果相同就可以,如果不同在递归。直到没有,就从头再来。
说时迟,那时快,小k马上画了一个图。重人恍然大悟,笑嘻嘻的回家了。
这里写图片描述

next[i]保存的 1————i-1 这个串的最长 相同 前缀和后缀,
遍历的过程,如果每次找到都取0,就是统计个数。(相当于找到一个后,母串子串都在当前左对齐,重新找。。qwq)
算法思想: 字符串的前后缀,问题转化为更小的问题(dp??递归?,反正就是酱紫)
附赠一个 代码 和kuangbin神的 模板。

 #include <iostream>
#include <bits/stdc++.h>
using namespace std;
/*
1 关于next数组的计算。
kmp之所以能够简化速度,是因为记录了模式串的最长公共前缀和后缀
*/
int nex[105];//计算 next数组,
// 并且 nex[i] 是 i-1串的 最长的(前缀和后缀相同的)长度
int ans;
void next(char s[]){
      //memset(nex,-1,sizeof(nex));
      int len=strlen(s);
      //cout<<"len "<<len<<endl;
      int i=0;//下面就要计算 next数组了
      int k=-1;//记录最大next
      nex[0]=-1;
      while(i<len){
          if(k==-1||s[i]==s[k]){
             i++;k++;
             nex[i]=k;//记住,nex i是i-1串的 最长前后缀。
          }
          else
            k=nex[k];//递归,在最长 前后缀中间继续寻找。
      }
}
void kmp(char a[],char  b[]){
     next(b);
     int len1=0;
     int len2=0;
     int siz1=strlen(a);
     int siz2=strlen(b);
     while(len1<siz1){
           while(len2>0&&a[len1]!=b[len2]){
                  len2=nex[len2];
           }// 第一次位置,
           if(a[len1]==b[len2])
              {len2++;}
           if(len2==siz2){
              ans++;
               //len2=nex[len2];// 寻找出现的次数。
               len2=0;// 让模式串从头开始找。
           }
           len1++;
     }
}
int main()
{   char a[1003],b[1003];
    while(~scanf("%s",a)){
       ans=0;
       if(strcmp(a,"#")==0){
         break;
         }
       scanf("%s",b);
       kmp(a,b);
       printf("%d\n",ans);
    }
    return 0;
}





kuangbin 模板
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000002;
int next[N];
char S[N], T[N];
int slen, tlen;

void getNext()
{
    int j, k;
    j = 0; k = -1; next[0] = -1;
    while(j < tlen)
        if(k == -1 || T[j] == T[k])
            next[++j] = ++k;
        else
            k = next[k];
            for(int i=0;i<tlen;i++)
                cout<<next[i]<<" "<<endl;

}
/*
返回模式串T在主串S中首次出现的位置
返回的位置是从0开始的。
*/
int KMP_Index()
{
    int i = 0, j = 0;
    getNext();

    while(i < slen && j < tlen)
    {
        if(j == -1 || S[i] == T[j])
        {
            i++; j++;
        }
        else
            j = next[j];
    }
    if(j == tlen)
        return i - tlen;
    else
        return -1;
}
/*
返回模式串在主串S中出现的次数
*/
int KMP_Count()
{
    int ans = 0;
    int i, j = 0;

    if(slen == 1 && tlen == 1)
    {
        if(S[0] == T[0])
            return 1;
        else
            return 0;
    }
    getNext();
    for(i = 0; i < slen; i++)
    {
        while(j > 0 && S[i] != T[j])
            j = next[j];
        if(S[i] == T[j])
            j++;
        if(j == tlen)
        {
            ans++;
            j = next[j];
        }
    }
    return ans;
}
int main()
{

    int TT;
    int i, cc;
    cin>>TT;
    while(TT--)
    {
        cin>>S>>T;
        slen = strlen(S);
        tlen = strlen(T);
        cout<<"模式串T在主串S中首次出现的位置是: "<<KMP_Index()<<endl;
        cout<<"模式串T在主串S中出现的次数为: "<<KMP_Count()<<endl;
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值