大话kmp! (模版测试题hdu1171)
2016-03-15 21:45
302 查看
kmp是数据结构里面关于字符串很重要的内容
关于kmp,网上有很多资料,但是有的人看了看懂了,有的看了还是看不明白
所以我自己还是要自己整理一下。
关于kmp求next数组,这是最难的,很难理解,尤其是那一句k=next[k],真的是让人费解
首先,kmp高效的原因是因为,进行了预处理,然后匹配时主串不回溯,使得kmp效率达到了O(n+m),比起普通的暴力匹配高效了不少。
首先我们来分析一下get_next函数,这个是得到next数组的函数。
i的含义是求next[i+1]的值,j的含义是上一个next[]的值是多少,比如上一个j是5,然后这时t[i]==t[j],就是表示此时的next[i+1]==6,因为next数组表示的是最长前缀后缀
j==-1的时候,next[i]=0,这个不难理解。
t[i]==t[j]的时候,更好,直接+1了。
当t[i]==t[j]的时候,很有可能这个答案是0,也可能介于0~j-1之间了,反正不会超过j了,因为唯一一次等于j+1的机会已经错过了(t[i]==t[j]),于是我们该怎么选择,于是先辈们就说,选择k=next[k]这个位置!
为什么?
http://www.rudy-yuan.net/archives/182/
我也想讲,但是这篇博客讲的很清楚,我自己在记事本上的图画的太丑了,实在是不好看清楚,就直接看这篇博客把!
这里的get_next函数还有下面注释部分的写法,其实都是一样的。
可以看到
else if(next_[j]==-1)
这一句有人会发现和网上的代码不一样,当然在这里,很多人写的是j==0,这里写成这样我觉得更好理解。
其实都是一样的,根据我们求next的代码,计算next[i]时,j是++过的,这就意味着除了j==0之外,没有别的next值会是-1,但是我写成这样,可以理解为,我们下面要将next[j]得值赋值给j,所以我们排出j==-1的情况,以免运行错误。当然这只是一种理解,正确而且比较官方有信服力的解答是,这条语句,如果s[i]和t[0]不想等,那么i就可以直接++了,不用在调整j的位置了,因为对于i位置,t串已经不想等了,可以直接进行下一次匹配了。
写代码:(这个代码是hdu1171的代码,纯kmp模版)
关于kmp,网上有很多资料,但是有的人看了看懂了,有的看了还是看不明白
所以我自己还是要自己整理一下。
关于kmp求next数组,这是最难的,很难理解,尤其是那一句k=next[k],真的是让人费解
首先,kmp高效的原因是因为,进行了预处理,然后匹配时主串不回溯,使得kmp效率达到了O(n+m),比起普通的暴力匹配高效了不少。
首先我们来分析一下get_next函数,这个是得到next数组的函数。
i的含义是求next[i+1]的值,j的含义是上一个next[]的值是多少,比如上一个j是5,然后这时t[i]==t[j],就是表示此时的next[i+1]==6,因为next数组表示的是最长前缀后缀
j==-1的时候,next[i]=0,这个不难理解。
t[i]==t[j]的时候,更好,直接+1了。
当t[i]==t[j]的时候,很有可能这个答案是0,也可能介于0~j-1之间了,反正不会超过j了,因为唯一一次等于j+1的机会已经错过了(t[i]==t[j]),于是我们该怎么选择,于是先辈们就说,选择k=next[k]这个位置!
为什么?
http://www.rudy-yuan.net/archives/182/
我也想讲,但是这篇博客讲的很清楚,我自己在记事本上的图画的太丑了,实在是不好看清楚,就直接看这篇博客把!
void get_Next(int m){ next_[0]=-1; for(int i=0,j=-1;i<m;){ if(j==-1||t[i]==t[j]){ i++;j++; next_[i]=j; } else j=next_[j]; } /* int j=0,k=-1; while(j<m){ while(k>=0&&t[j]!=t[k]){ k=next_[k]; } j++;k++; next_[j]=k; } */ }
这里的get_next函数还有下面注释部分的写法,其实都是一样的。
int kmp_solve(int n,int m){ get_Next(m); int i=0,j=0; while(i<n&&j<m){ if(s[i]==t[j]){ i++;j++; } else if(next_[j]==-1){ i++; } else j=next_[j]; } if(j>=m)return i-j+1; return -1; }
可以看到
else if(next_[j]==-1)
这一句有人会发现和网上的代码不一样,当然在这里,很多人写的是j==0,这里写成这样我觉得更好理解。
其实都是一样的,根据我们求next的代码,计算next[i]时,j是++过的,这就意味着除了j==0之外,没有别的next值会是-1,但是我写成这样,可以理解为,我们下面要将next[j]得值赋值给j,所以我们排出j==-1的情况,以免运行错误。当然这只是一种理解,正确而且比较官方有信服力的解答是,这条语句,如果s[i]和t[0]不想等,那么i就可以直接++了,不用在调整j的位置了,因为对于i位置,t串已经不想等了,可以直接进行下一次匹配了。
写代码:(这个代码是hdu1171的代码,纯kmp模版)
#include<iostream>
using namespace std;
#include<cstring>
#include<cstdio>
const int maxn=1000005;
int s[maxn];
int t[maxn];
int next_[maxn];
void get_Next(int m){ next_[0]=-1; for(int i=0,j=-1;i<m;){ if(j==-1||t[i]==t[j]){ i++;j++; next_[i]=j; } else j=next_[j]; } /* int j=0,k=-1; while(j<m){ while(k>=0&&t[j]!=t[k]){ k=next_[k]; } j++;k++; next_[j]=k; } */ }
int kmp_solve(int n,int m){ get_Next(m); int i=0,j=0; while(i<n&&j<m){ if(s[i]==t[j]){ i++;j++; } else if(next_[j]==-1){ i++; } else j=next_[j]; } if(j>=m)return i-j+1; return -1; }
int main(){
int T;
cin>>T;
while(T--){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",&s[i]);
}
for(int i=0;i<m;i++){
scanf("%d",&t[i]);
}
int ans;
if(n>=m)
ans=kmp_solve(n,m);
else ans=-1;
cout<<ans<<endl;
}
return 0;
}
相关文章推荐
- 手机震动的节奏(Vibrator对象及周期运用)
- Java学习笔记(反射+枚举类)
- coderforce 510c Fox And Names(两种写法)
- Delphi访问活动目录
- OC字符串NSString
- 无厘头知识--前端笔记
- Android ApiDemos示例解析(45):App->Text-To-Speech
- Struts学习笔记(三):Ajax +json+JQuery的综合使用
- 搭建Pxe服务器无人听应答全自动安装VMWare esxi 6.0系统
- UCenter单点登录,同步登录,同步登出原理
- VS2013——error C4996: 'std::_Uninitialized_copy0':
- iOS,Android,WP, .NET通用AES加密算法
- 基础总结篇之一:Activity生命周期
- next_permutation的思想和用法
- 需求获取常见的方法是进行客户访谈,结合你的实践谈谈会遇到什么问题,你是怎么解决的?
- BZOJ 2190 仪仗队
- 多阶矩在图像中的含义(方差,偏度,峰度)
- PhpStorm +xdebug 调试安装配置注意事项
- MFC CArchive Serialize 序列化类 设计和使用
- java SE基础(TCP Socket通信)