KMP(字符串查找)(hihocoder)
2016-09-08 15:04
232 查看
题目来源:hihocode-KMP
思路:
KMP算法,在hihocoder也有讲解,这里说一下我的理解。
一般字符串匹配查找时,我们一般会使用这种方法
但这种方法是可以改进的,这种方法在进行匹配的时候是有很多重复的匹配操作的;
例如有原串 babababcbab 模式串 bababb
在第一次匹配时,从s[0]开始,如图,会在s[5]不匹配
![](http://img.blog.csdn.net/20160908151841956?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后进行第二次匹配,从s[1] 开始,如图,会在s[5]不匹配
![](http://img.blog.csdn.net/20160908151919612?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在这两次中,第二次的匹配是从 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]
一直到遍历到串尾
这是代码:
思路:
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 字符串匹配 SDNU 1100 字符串查找 HDU 2087 剪花布条
- 【算法学习】KMP查找匹配字符串
- KMP 算法并非字符串查找的优化 [转]
- kMP用于字符串的查找
- KMP 算法并非字符串查找的优化
- 字符串查找-普通方法和KMP(普拉特操作)介绍
- Lintcode—13. 字符串查找(普通方法和KMP方法两种方法解决)
- 字符串查找之KMP
- C语言 算法"KMP" 查找字符串 ”模式匹配“
- 用kmp查找字符串的循环节
- KMP 字符串查找
- KMP字符串查找
- 字符串查找-bf kmp
- #旧代码# 基本算法练习:常见排序/KMP字符串查找/链表反转
- LintCode 字符串查找(暴力法+KMP)
- 子字符串查找或匹配(暴力法,KMP)
- KMP快速查找字符串
- KMP 算法并非字符串查找的优化 [转]
- C++实现查找字符串中的数字,并输出
- Java中字符串中子串的查找共有四种方法(indexof())