码迷,mamicode.com
首页 > 编程语言 > 详细

吃透烤馍片算法

时间:2015-04-21 17:25:46      阅读:282      评论:0      收藏:0      [点我收藏+]

标签:

kmp又称烤(k)馍(m)片(p)算法,实际上是通过模式串本身的特性来优化模式串的匹配

next[j]表示j之前的字符串中有长度为next[j]的相同前缀后缀

失配时移动位置=失配字符所在位置-失配字符对应的next值,即j-next[j]

next[0]=-1 表示当0位置失配时,要将字符串向右移1位

1.求next数组:

next[j]表示j之前的字符串中有长度为next[j]的相同前缀后缀

求next数组的过程相当于模式串自己和自己匹配

void getNext()
{
    int i,j;
    i=0;j=-1;next[0]=-1;
    while(i<tlen)
    {
        if(j==-1||t[i]==t[j])next[++i]=++j;
        else j=next[j];
    }
}

2.用next数组求最小循环节的长度:

如果一个字符串可以由k个某一子串组成,那么 这个字符串的最小循环节为该子串,循环周期为k。

如abcd最小循环节为abcd,abcabc最小循环节为abc

怎样求最小循环节?   如abcabcabc

next数组为:

a   b   c   a   b   c   a   b   c     
0 1 2 3 4 5 6 7 8 9
-1 0 0 0 1 2 3 4 5 6

 

 

 

next[len]是一个很特殊的值,它表示整个字符串前后缀相同的最大长度

如上栗子,next[len]=6

说明 字符串中 a[0]-a[5] 与 a[3]-a[8]是完全相同的

也就是说 a[0]-a[2]与 a[3]-a[5]是完全相同的,a[3]-a[5] 与 a[6]-a[8]是完全相同的,

也就是说该字符串可以由 a[0]-a[2]循环3次组成

所以只要tlen%(tlen-next[tlen])==0&&next[tlen]!=0 字符串就可以由长度比它小的子串组成

否则循环节为它本身

while(~scanf("%s",t))
{
    tlen=strlen(t);
    getNext();
    if(tlen%(tlen-next[tlen])==0&&next[tlen]!=0)printf("%d\n",tlen-next[tlen]);
    else printf("%d\n",tlen);
}

3.kmp求模式串第一次出现的位置

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;
}

4.kmp求模式串在匹配串中出现的次数(有可重叠和不可重叠之分)

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=0;
            //如果子串可以重叠j=next[j];
        }

    }
    return ans;
}

 

烤(k)馍(m)片(p)完整模板:

技术分享
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define N 1000010
using  namespace std;
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];
    }
}
//返回模式串t在主串s中首次出现的位置
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;
}
//返回模式串t在主串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++)
    {
    //    printf("***%d %d\n",i,j);
        while(j>0&&s[i]!=t[j])j=next[j];
        if(s[i]==t[j])j++;
        if(j==tlen)
        {
            ans++;
            j=0;
            //如果子串可以重叠j=next[j];
        }

    }
    return ans;
}

int main()
{
    //用next数组求循环节的长度
     while(~scanf("%s",t))
    {
        tlen=strlen(t);
        getNext();
       // for(int i=0;i<=tlen;i++)printf("%d %d\n",i,next[i]);
        if(tlen%(tlen-next[tlen])==0&&next[tlen]!=0)printf("%d\n",tlen-next[tlen]);
        else printf("%d\n",tlen);
    }
/*
  //kmp模式匹配
    while(~scanf("%s%s",s,t))
    {
        slen=strlen(s);
        tlen=strlen(t);
      //  printf("%d\n",kmp_Index());
        printf("%d\n",kmp_Count());
      //  for(int i=0;i<=tlen;i++)printf("%d %d\n",i,next[i]);
    }
*/
    return 0;
}
View Code

 

吃透烤馍片算法

标签:

原文地址:http://www.cnblogs.com/kylehz/p/4392945.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!