kmp算法研究

kmp算法是一种字符串的匹配算法,平常我们写一个字符串的匹配算法是非常容易的,所谓的字符匹配,就是说当已知一个主串S,并且有一个字符串T,我们看看T这个字符串是否在S串中出现过,当然说的有些白话了~~,我看了许多关于kmp的资料,网上有很多,但很长很长的时间的一段时间里,都没有消化。心里非常内疚,其实到现在,我都不确定自己是否真的理解了kmp。但时间一长,就萌生了把各个kmp资料整理一下的冲动,下面就是我整理的,或许你看了之后还不明白,这正常~~

 

首先,我先说一下普通的字符串的匹配算法,我相信,这样的算法一个正常的ACMer都会写出来,并且如果S很短,用这个算法就行了,就是一一比较,若出现不相等就回去重新比,假如S[i-j....i]与T[0....j]都成功了!我们必然要比较i+1和j+1,若不相等,则就要从头比较(这也是最浪费时间的)也就是比较S[i-j+1]与T[0],废话不多说给出代码~~~

注:S[m...n]表示所以m到n区间内的字符;

代码

int Index_BF(char S[],char T[],int pos)
{
    int i=pos;//从pos位置开始比较,一般初始化为0
    int j=0;
    while(S[i+j] != '\0' && T[j] !='\0')
        if(S[i+j]==T[j])
        j++;//匹配则继续匹配
        else
        {
            i++;
            j=0;//不匹配就要重新开始匹配
        }
        if(T[j]=='\0')
            return i;//匹配成功 返回下标
        else
            return -1;//说明匹配成功
}
但是,如果S这个字符串很长呢?那么匹配就要花费很长时间,我们先来看看我们这个kmp是如何提高匹配的效率的或者说它是如何工作的:首先我们还是给出S和T我们的目的是看看T是否在S中出现过,需要两个“指针” i,j。我们比较是S[i-j...i]与T[0,j]肯定完全相等,如果S[i+1]与T[j+1]还相等,我们不多说,肯定要i++,j++;这和普通的匹配没什么不同,但如果不相等即:S[i+1]与T[j+1]失配了,我们怎么办?kmp算法的思想是调整j的值(减小j值),假设改完后的j为j'那么,这个j'会保持

S[i-j'...i]与T[0...j']匹配,只不过字符数量减小了,但不至于和上面那个普通匹配算法一样已经匹配的字符数量为0。但问题又来了,我们如何去改变j的值,j的值肯定是越大越好。这就需要我们的next函数,其实它就是一个一维数组,里面的值就间接或直接告诉我们该怎么对j进行处理。现在我证明的问题是这个next数组各个值为什么是正确的,也就是说我们怎么保证S[i-j'..i]和T[0...j']是匹配的?(我认为这个是最重要的)

我们先说一下next取值,next的下标索引对应着T字符串的数组的索引,例如next[4]=3,就说明T[4]对应的next函数值为3,如果S[m]与T[4]不匹配,我们就要利用next[4]进行调整。正规next的取值有4种类型(我们假设S[m]和T[n]不相等了~~

 

1.next[n]=-1 这就是说S[m]和T[0]比较过,相等,因此直接比较S[m+1]T[0]即可。

2.next[n]=0 表示让你直接比较T[0]S[m]即可。

3.next[n]=k>0但k<n,表示S[m]的前k个字符与T开始的k个字符已经比较过了现在比较T[k]S[m]是否相等。

 

我在给出next是怎样求出来的?(next 的值对应了T的每个字符)

<1>next[0] = -1 我们规定next[0]一定为-1

<1>next[j] = -1  表示模式串T下标为j的字符,如果T[j]==T[0],(如果T[j]!=T[0],则next[j]肯定不等于-1);并且j的前面的k个字符与开头的k个字符不同(1<=k<j)或者相同但是T[k]==T[j];

<3>next[j]=k 表示 模式串T的下标为j的字符T[j]如果j前面的k个字符与开头的k个字符相等并且T[k]!=T[j]则next[j]=k;例如:T="abcdabcd" 则next[7]=3因为前面3个字符(abc)和j=7前面3个字符(abc)相等并且T[6]!=T[3]; 

再例如T="abababc" 则next[6]=4 因为开头的4个字符(abab)与j=6前面的4个字符(abab)相等,并且T[6]!=T[4].

<4>除了上述的情况next[j]=0;

最后我们根据next的值就能确定实际要回溯的位置了,怎样得到T的next函数代码如下:

void get_nextval(char*T)
{
    int j=0,k=-1;
    next[0]=-1;
    while(T[j]!='\0')
    {
        if(k==-1||T[j]==T[k])
        {
            ++j;
            ++k;
            if(T[j] != T[k])
                next[j]=k;
            else
                next[j]=next[k];
        }
        else
            k=next[k];
    }
}
另一种得到next的写法:
void getNext(char *pattern,char*next)
{
    next[0]=-1;
    int k=-1,j=0;
    while(pattern[j] != '\0')
    {
        if(k != -1 && pattern[k]!=pattern[j])
            k=next[k];
        ++j;
        ++k;
        if(pattern[k]==pattern[j])
            next[j]=next[k];
        else
            next[j]=k;
    }
}


于是kmp的算法模板

int KMP(char *Text,char *Pattern)const
{
    if(!Text||!Pattern!!Pattern[0]=='\0'||Text[0]=='\0')
        return -1;
    int len=0;
    char*c=Pattern;
    while(*c++!='\n')
        ++len;
    int *next=new int [len+1];
    getNext(Pattern,next);//得到Pattern的next函数值
    int index=0,i=0,j=0;
    while(Text[i]!='\0' &&Pattern[j]!='0')
    {
        if(Text[i]==Pattern[j])
        {
            i++;
            j++;
        }
        else
        {
            if(next[j]!=-1)
                j=next[j];//即使为零也没关系
            else
            {
                j=0;
                i++;
            }
            index=i;
//如果到这说明又要重新匹配了,因此要保留刚开始匹配的地方
        }
    }
    delete []next;
    if(Pattern[j]=='\0')
        return index;//匹配成功
    else
        return -1;
}


 


如果还是不太理解,我建议做两个纸带一个代表S一个代表T然后手动移动一下。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值