Sweety

Practice makes perfect

导航

KMP复习整理

Posted on 2014-09-01 20:55  蓝空  阅读(127)  评论(0编辑  收藏  举报

现在看来下面写的不是很好,以后在整理

next数组的求法及原理应该注意的是由于在编号的时候将next中的第一个元素编排成0了,因此next[i]其实记录不但是与从开头的对称数,并且是i+1所要匹配的元素的下标

当出现失配的情况时,如下图在str[14]!=str[7]的时候,就接着进行如下操作


也就是说再接着从失配的位置(7)之前的那个字母在重新匹配,但是这时实际应该是从开头的那个位置来说的所以是匹配的3位置,而不是6位置,再好好想想。。。

#include <iostream>
#include <cstring>
using namespace std;
int main ()
{
 char str[1000];
 cin>>str;
 int next[1000];
 int len=strlen(str);//模式字符串长度。
 next[0]=0;
 for(int i=1; i<len; i++)
 {
  int k=next[i-1];
  while( str[i] != str[k]  &&  k!=0 )
   k=next[k-1];     //继续递归
  if( str[i] == str[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++
   next[i]=k+1;
  else
   next[i]=0;       //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0
 }
 return 0;}

 

 

 

较为低级的求next数组的方法

//这中算法是字母的下标和next数组中的下标正好相等了,也就是说这样求的next数组所储存的值
//正好相当于从开头开始计算的连续字母数,但是这样虽看起来挺好的,都储存了各自的next数组量
//但是不好用,在查询的时候没有结束标志,整体来说这样的话操作 相对容易

#include <iostream>
#include <cstring>
using namespace std;
int main ()
{
 char str[1000];
 cin>>str;
 int next[1000];
 int len=strlen(str);//模式字符串长度。
 next[0]=0;
 int i;
 for(i=1; i<len; i++)
 {
  int k=next[i-1];//k是记录的当前字母的前一个
  while( str[i] != str[k]  &&  k!=0)//如果没有找到相同的且不是到next[]为零或者是第一个(即为第一个数)
   k=next[k-1];     //继续递归
  if( str[i] == str[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++
   next[i]=k+1;
  else
   next[i]=0;       //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0
 }
    for(i=0; i<len; i++)
  cout<<str[i]<<"  ";
  cout<<endl;
    for(i=0; i<len; i++)
  cout<<next[i]<<"  ";
 return 0;

}

<img src="//img-blog.csdn.net/20140912193849256?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxNDY2NTAxMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />


 

以上的是这种方式不好找结束条件,不是很好,下面的稍作修改就有了...

 

使用较为低级的方法求next数组的方法匹配

 

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxsize=100;
void getnext(string str,int next[])
{
 int len=str.length();//模式字符串长度。
 next[0]=-1;
 next[1]=0;
 int i;
 for(i=1; i<len; i++)
 {
  int k=next[i-1];
  while( str[i] != str[k]  &&  k!=0 && k!=-1 )//如果没有找到相同的且不是到next[]为零或者是第一个(即为第一个数)
   k=next[k-1];     //继续递归
  if( str[i] == str[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++
                        //这个地方如果k是-1的时候不会报错,并且也正好不满足这个条件,所以这样就会运行处正确的结果
   next[i+1]=k+1;
  else
   next[i+1]=0;   //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0
 }
}
void kmpindex(string s,string t)
{
 int next[maxsize],i=0,j=0;
 memset(next,-1,sizeof(next));
 getnext(t,next);
 j=0;
                    /*  while(j<t.length())
                      {
                       cout<<t[j]<<"   ";
                       j++;
                      }
                       j=1;
                       cout<<endl;
                      while(j<t.length())
                      { 
                       cout<<next[j]<<"   ";
                       j++;
                      } */
 int m=1;
 j=0;
 while( i<s.length() && j<int(t.length()) )//s为原串(主串 目标串),t为匹配串(模式串)
 { 
  if( j==-1 || s[i] == t[j] )
   i++,j++;
  else
   j=next[j],m++;//返回的时候在增加一趟
  // 其实,为什么没有用到next[]最后一个,这是因为在每一次用到的是上一个字母的位置,至于最后一个是没有用到的
 }
                                                                                  //    cout<<endl<<"*******************************************"<<endl;
 if(j=int(t.length() ))               //说明匹配串已经遍历完毕了,也就是说匹配成功了
       cout<<m<<' '<<i-t.length()+1<<endl;    //匹配成功时第一个字母的位置   
 else
  cout<<0<<endl;
                                                                                      // cout<<"*******************************************"<<endl;
}
int main()
{
 string a,b;
 cin>>a>>b;
 kmpindex(a,b);
 return 0;
}



/*一直不太清楚为什么在next数组中没有左最后一个的值,其实原因很简单,就是用不到

因为在匹配时是从该字母的上一个的*/

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int maxsize=100;
void getnext(string t,int next[])
{
 int j,k;
 j=0; k=-1;
 next[0]=-1;
 while(j<t.length())
 {
  if(k==-1 ||t[j]==t[k])
  {
   j++;
   k++;
   next[j]=k;
  }
  else
   k=next[k];
 }
                                                                 // cout<<"j"<<"   "<<j<<endl;
}
void kmpindex(string s,string t)
{
 int next[maxsize],i=0,j=0;
 memset(next,-1,sizeof(next));
 getnext(t,next);
 j=0;
 cout<<' ';
                /* while(j<t.length())
                 {
                  cout<<t[j]<<"   ";
                  j++;
                 }
                  j=0;
                 cout<<endl;
                 while(j<t.length())
                 { 
                  cout<<next[j]<<"   ";
                  j++;
                 }*/
 int m=1;
 j=0;
 while( i<s.length() && j<int(t.length()) )
 { 
  if( j==-1 || s[i] == t[j] )
   i++,j++;
  else
   j=next[j],m++;//返回的时候在增加一趟
                    //注意第一个next是-1,其他的都像后推
 }
                                                               //cout<<endl<<"*******************************************"<<endl;
 if(j>=int(t.length() ))
        cout<<m<<' '<<(i-t.length())+1<<endl;
 else
  cout<<0<<endl;
                                                                  // cout<<"*******************************************"<<endl;
}
int main()
{
 string a,b;
 cin>>a>>b;
 kmpindex(a,b);
 return 0;
}