您的位置:首页 > 其它

KMP(字符串查找)(hihocoder)

2016-09-08 15:04 232 查看
题目来源:hihocode-KMP

思路:

         KMP算法,在hihocoder也有讲解,这里说一下我的理解。

         一般字符串匹配查找时,我们一般会使用这种方法

//以i开头,开始和模式串进行匹配(s为原串,p为模式串)
for(int i=0,j=0;i<s.length();++i){
while(i<slen && j<plen && s[i] == p[j])i++,j++;
i = i - j;
if(j == p.length())return i;
}
      这种算法的最坏时间复杂度是 原串的长度成以模式串的长度 即 s.length()*p.length() ;

      但这种方法是可以改进的,这种方法在进行匹配的时候是有很多重复的匹配操作的;

      例如有原串 babababcbab 模式串 bababb

          在第一次匹配时,从s[0]开始,如图,会在s[5]不匹配 

                               

       

           然后进行第二次匹配,从s[1] 开始,如图,会在s[5]不匹配

                                 


             在这两次中,第二次的匹配是从 s[1]开始,而s[1]显然和p[0]不相等,这实际上这可以由第一次的匹配得到的;

             可见这种方法的冗余在某些情况下可能会更大。

         故而有了KMP算法。

         如果在匹配不成功后,我可以不将 i 改变,而是保持 i ,继续和 p 某一位置 k 进行匹配,这样算法不就成了线性的了吗?

          假设这个位置记为next[j],也就是说,如果匹配不成功 i 不变,而 j 变为 next[j];

          那么这个next[j]怎么求呢?

          实际上保持 i 不变 ,而使 i 继续和 p 的某一位置 k 继续匹配,

          当且仅当 在 p.substring(0,k) == p.substring(k+1,j-1),如果有多个当然去k最大的那一个;   (p.substring(i,j) 表示以i开始,j结尾的子串)

          下面讲具体的做法

                     next[j]表示当 s[i] != p[j] 时 j 应该向左移动到那里;

                    显然 next[0] = -1 表示 s[i] != p[0] 时,此时 i应加一,即 应该 向右移动一个距离

                    显然 next[1] = 0;

                    然后从p[2]开始计算这时需要比较p[0]和p[1]

                                假设p[0]和p[1]相等,那么 next[2]就应该为1

                                 再去求next[3]时, 由于p[1] == p[0],这时只用继续比较p[2]和p[1]是否相等了

                    由上面的简但陈述可以看出,这其实就是模式串和自身的匹配过程;

                    只需要建立两个计数器i,j,i从1开始,j从0开始

                                                           如果 p[i] == p[j]  则 next[i+1] = j+1,i++,j++;

                                                           否则 j = next[j]

                                                           一直到遍历到串尾

这是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 2100700

//截取原串的所有长度等于模式串p的所有子串;
int cmp0(string s,string p){
//以i开头,长度为p.length()
for(int i=0;i<s.length();++i)
if(s.substr(i,p.length()) == p)return i;
return -1;
}

int cmp0(char *s,char *p){
int slen=strlen(s),plen=strlen(p);
for(int i=0;i<slen;++i){
int j=i,k=0;
while(j<slen && k<plen && s[j] == p[k])j++,k++;
if(k == plen)return i;
}
return -1;
}

//使用string类进行查找
void Next(string p,int *next){
next[0]=-1,next[1]=0;
for(int i=1,j=0;i<p.length();){
if(j == -1 ||p[i] == p[j]){
++i,++j;
next[i]=j;
}else j=next[j];
}
}

int kmp(string s,string p){
int *next = new int[p.length()+1],cnt=0;
Next(p,next);
for(int i=0,j=0;i<s.length();){
while(j == -1 || i<s.length() && j<p.length() && s[i] == p[j])++i,++j;
if(j == p.length()){
cnt++;
j=next[j];
}
else j=next[j];
}
return cnt;
}

//使用字符数组查找
void Next(char *p,int *next,int plen){
next[0]=-1,next[1]=0;
for(int i=1,j=0;i<plen;){
if(j == -1 || p[i] == p[j]){
++i,++j;
next[i]=j;
}else j=next[j];
}
}
int kmp(char *s,char *p){
int cnt=0,slen,plen,*next;
slen=strlen(s),plen=strlen(p);
next = new int[plen+1];
Next(p,next,plen);
for(int i=0,j=0;i<slen;){
while(j == -1 || i<slen && j<plen && s[i] == p[j])++i,++j;
if(j == plen){
cnt++;
j=next[j];    //这一步很关键,不然会超时
}
else j=next[j];
}
return cnt;
}

char s[MAX],p[MAX];
int main(){
int n;
cin>>n;
while(n--){
cin>>p>>s;
cout<<kmp(s,p)<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kmp 算法