现在看来下面写的不是很好,以后在整理
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="http://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; }