HDU 2087剪花布条(KMP入门模板题)

一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
Sample Input
abcde a3
aaaaaa aa
#
Sample Output
0
3

就是在主串中找子串 ,子串在主串中不重叠。直接套模板。

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
假设主串为’s[1],s[2]…s[n]’,模式串为’p[1],p[2]…p[m]’。当匹配过程中产生“失配”(即s[i]≠p[j])时,模式串“向右滑动”可行的距离多远。换句话说,当主串中第i个字符与模式中第j个字符“失配”(即比较不等)时,主串中第i个字符(i指针不回溯)应与模式中哪个字符再比较?
假设此时应与模式中第k(k < j)个字符继续比较,则模式中前k-1个字符的子串必须满足下列关系式①且不可能存在k’< k满足下列关系式①。

                    ①    ‘p[1]p[2]…p[k-1]’=‘s[i-k+1]s[i-k+2]…s[i-1]’

而已经得到的“部分匹配”的结果是

                    ②    ‘p[j-k+1]p[j-k+2]…p[j-1]’=‘s[i-k+1]s[i-k+2]…s[i-1]’

由式①和式②推得下列等式

                    ③    ‘p[1]p[2]…p[k-1]’= ‘p[j-k+1]p[j-k+2]…p[j-1]’

反之,若模式串中存在满足式③的两个子串,则当匹配过程中,主串中第i个字符与模式中第j个字符比较不等时,仅需将模式向右滑动至模式中第k个字符和主串中第i个字符对齐,此时,模式中头k-1个字符的子串‘ p[1]p[2]…p[k-1]’必定与主串中第i个字符之前长度为k-1的子串‘ s[i-k+1]s[i-k+2]…s[i-1]’相等,由此,匹配仅需从模式中第k个字符与主串中第i个字符比较起继续进行。

Next数组:
next[i](i从1开始算)代表着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的个数。
构造next数组使用的基本方法是递推,要计算当前第i位字符的next值即计算从字符串开始至第i位前(不包括第i位)的字符串的最长前缀与后缀重复数量。
规定第0位(第一个字符)的next值是0或-1(有不同构造next函数的方法)。
第i+1位的next值分两种情况讨论:

①当p[i]=p[j]时,前缀和后缀相等,最大长度可以延续因此next[++i]=next[++j]

②当p[i]≠p[j]时,前缀和后缀匹配到此不等,那么可以利用已计算好的next值,将j回溯,直至找到可匹配的重复前缀后缀。也就是j=next[j]

代码如下:

#include<stdio.h>
#include<string.h>
char S[1004],T[1004];
void kmp_pre(char x[],int m,int next[])///子串预处理next数组
{
    int i,j;
    j=next[0]=-1;
    i=0;
    while(i<m)
    {
        while(j!=-1&&x[i]!=x[j])j=next[j];
        next[++i]=++j;
    }
}
int kmp_count(char x[],int m,char y[],int n)
{
    int i=0,j=0,next[1004],ans=0;
    kmp_pre(x,m,next);
    while(i<n)
    {
        while(j!=-1&&x[j]!=y[i])j=next[j];
        i++;j++;
        if(j>=m)
        {
            ans++;
            j=0;
        }
    }
    return ans;
}
int main()
{
    while(scanf("%s",S)!=EOF)
    {
        if(S[0]=='#')break;
        scanf("%s",T);
        printf("%d\n",kmp_count(T,strlen(T),S,strlen(S)));
    }
}
posted @ 2018-03-09 19:22  KuroNekonano  阅读(136)  评论(0编辑  收藏  举报