KMP模板

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。

KMP字符串匹配算法,得到next数组o(n),匹配o(n+m)

int Next[N];
char mo[N];
char str[N];

void Get_next()
{
    int len=strlen(mo);
    int j=-1;
    Next[0]=-1;
    int i=0;
    while(i<len)
    {
        while(j!=-1&&mo[i]!=mo[j])
            j=Next[j];
        Next[++i]=++j;
    }
}


//这种预处理模式串的方法更快
void Get_nextval()
{
    int len = strlen(mo);
    int i,j;
    j = Next[0] = -1;
    i = 0;
    while(i<len)
    {
        while(j!=-1&&mo[i]!=mo[j])
            j = Next[j];
        if(mo[++i] == mo[++j])
            Next[i]=Next[j];
        else
            Next[i]=j;

    }
}

//返回模式串T在主串S中首次出现的位置
//返回的位置是从0开始的。
int KMP_Index()
{
    int len = strlen(str);
    int molen = strlen(mo);
    int i = 0, j = 0;
    while(i <  len&& j < molen)
    {
        if(j == -1 || str[i] == mo[j])
        {
            i++; j++;
        }
        else
            j = Next[j];
    }
    if(j == molen)
        return i - molen;
    else
        return -1;
}

//返回模式串在主串S中出现的次数
int Kmp_count()
{
    int len1=strlen(str);
    int len2=strlen(mo);
    int ans=0;
    int i=0,j=0;
    while(i<len1)
    {
        while(str[i]!=mo[j]&&j!=-1)
            j=Next[j];
        i++;
        j++;
        if(j>=len2)
        {
            ans++;
            j=Next[j];
        }
    }
    return ans;
}

//循环节问题
//重要的性质len-next[i]为此字符串的最小循环节(i为字符串的结尾),即循环周期
//另外如果len%(len-next[i])==0&&len!=(len-next[i]),此字符串的循环次数为len/(len-next[i])的循环串;
void Is_Cycle()
{
    //示例代码,求最少补几个字符串得到>=2的循环串
    int len = strlen(mo);  
    int length = len - Next[len];  //最小循环节长度  
    if(len%length==0&&length!=len) //刚好循环完,不必再补元素  
        printf("0\n");  
    else  
        printf("%d\n",length-len%length);  //未循环完补length-(len%length)个元素
} 


KMP算法,核心在于怎样得到next数组。

next数组的含义就是一个固定字符串的最长前缀和最长后缀相同的长度。

在某次匹配失败的时候,即str[i]!=mo[j]的时候,我们就直接将j变成next[j]的位置,为什么可以这样变,因为能匹配到这个位置,说明p1p2...p(j-1) = s(i-j+1)s(i-j+2)...s(i-1),如果我们找到最长的最长前缀和最长后缀相同的长度k,即p1p2...pk  = p(j-k+1)p(j-k+2)...p(j-1),那么我们的s(i-k+1)s(i-k+2)...s(i-1) = p1p2...pk-1了,也就匹配成功了,只需将j指针回朔,而i指针不变来降低复杂度了。

这里写了两种得到next数组的方法,第一种Get_next()就是直接按上面那种方法得来的,如果不等就一直回溯,否则next[++i]=++j。

然而仍然有缺陷,比如模式串aaaab和匹配串aaabaaaab按照上面得到底next数组为next[]={-1,0,1,2,3},在i=3的时候(从0开始),j会回溯成2,1,0,三次变化,然而这都是不需要的。因为他们都是相同的字符,所以在这里匹配不成功,回溯到next[j]的位置仍然不成功。所以就有了第二种改进一点的算法,如果回溯位置字符相同,则直接next[i]=next[j],就可以跳过当前位置了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值