KMP学习笔记
2016-05-08 22:45
302 查看
KMP算法的精髓就是next数组,必须充分理解这个next数组。
next[j]的含义就是j的真前缀中能够自匹配的最大前缀和后缀,相当于在失配的情况下
能够排除很多不必要的匹配过程。
构造next数组用递推:
但是在之后的匹配中还是会有很多不必要的匹配,
比如0 0 0 0 0 0 0 1个文本串和0 0 0 1这个模式串,在对齐位置
0 0 0 0 0 0 0 1
0 0 0 1 下,每一次借助next数组模式串只会移动一格,本质上是因为没有应用
失配情况下相同的字符必然也失配这种规律,所以我们修改下next数组,把next[j]表示
为j的真前缀中能够自匹配的的前缀和后缀并且这个前缀的末尾字符和后缀的末尾字符不
同,这就是扩展kmp,让然用上面的文本串和模式串,在对对齐位置
0 0 0 0 0 0 0 1
0 0 0 1 下发成失配时,模式串会直接移动到
0 0 0 0 0 0 0 1
0 0 0 1,这样就避免了很多次不必要的匹配。
容易看出,在字符集规模越小的情况下扩展KMP的优势更加明显。
在原来的next数组的代码中只需改变一行就可以实现扩展KMP
然后返回第一次匹配位置的主算法就很显然了
HDU 1711:点击打开链接
最裸的KMP,直接借用上面的代码:
next[j]的含义就是j的真前缀中能够自匹配的最大前缀和后缀,相当于在失配的情况下
能够排除很多不必要的匹配过程。
构造next数组用递推:
void get_next (int *p) { int t; t = next[0] = -1; int j = 0; while (j < m) { if (t < 0 || p[j] == p[t]) {//匹配 j++, t++; next[j] = t; } else //失配 t = next[t]; } }
但是在之后的匹配中还是会有很多不必要的匹配,
比如0 0 0 0 0 0 0 1个文本串和0 0 0 1这个模式串,在对齐位置
0 0 0 0 0 0 0 1
0 0 0 1 下,每一次借助next数组模式串只会移动一格,本质上是因为没有应用
失配情况下相同的字符必然也失配这种规律,所以我们修改下next数组,把next[j]表示
为j的真前缀中能够自匹配的的前缀和后缀并且这个前缀的末尾字符和后缀的末尾字符不
同,这就是扩展kmp,让然用上面的文本串和模式串,在对对齐位置
0 0 0 0 0 0 0 1
0 0 0 1 下发成失配时,模式串会直接移动到
0 0 0 0 0 0 0 1
0 0 0 1,这样就避免了很多次不必要的匹配。
容易看出,在字符集规模越小的情况下扩展KMP的优势更加明显。
在原来的next数组的代码中只需改变一行就可以实现扩展KMP
void get_next (int *p) { int t; t = next[0] = -1; int j = 0; while (j < m) { if (t < 0 || p[j] == p[t]) {//匹配 j++, t++; next[j] = (p[j] != p[t] ? t : next[t]); } else //失配 t = next[t]; } }
然后返回第一次匹配位置的主算法就很显然了
int kmp () { get_next (P); int i = 0, j = 0; while (i < n && j < m) { if (j < 0 || T[i] == P[j]) { i++, j++; } else { j = next[j]; } if (j == m) { return i-j+1; } } return -1; }
HDU 1711:点击打开链接
最裸的KMP,直接借用上面的代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define maxn 1111111
int P[maxn], T[maxn];
int n, m;
#define next Next
int next[maxn];
void get_next (int *p) { int t; t = next[0] = -1; int j = 0; while (j < m) { if (t < 0 || p[j] == p[t]) {//匹配 j++, t++; next[j] = (p[j] != p[t] ? t : next[t]); } else //失配 t = next[t]; } }
int kmp () { get_next (P); int i = 0, j = 0; while (i < n && j < m) { if (j < 0 || T[i] == P[j]) { i++, j++; } else { j = next[j]; } if (j == m) { return i-j+1; } } return -1; }
int main () {
int cas;
cin >> cas;
while (cas--) {
scanf ("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf ("%d", &T[i]);
for (int i = 0; i < m; i++)
scanf ("%d", &P[i]);
printf ("%d\n", kmp ());
}
return 0;
}
相关文章推荐
- advanced JavaScript Skills ——Require.js(二)
- NSURLSESSION的使用
- Git仓库创建
- 20145314郑凯杰 《Java程序设计》实验五 实验报告
- 学习web前端之神器sublime text 3
- 解读route命令
- java 创建线程的两个方法
- NSURLSESSION使用实战教程
- 第一题博客
- Java基础——王阔
- 使用 IDEA 创建 Maven Web 项目 (异常)- Disconnected from the target VM, address: '127.0.0.1:59770', transport: 'socket'
- 20150411--Dede二次开发-02
- Python第三方包 requests还是urllib?
- jquery实现左右无缝轮播图
- python爬虫抓取电影天堂最新电影
- 绝对详细!Nginx基本配置、性能优化指南
- 关于Android开机启动的小坑
- 介绍NSURLSESSION网络请求套件
- 20145329 《Java程序设计》实验五总结
- android开发之路之一--坚持写博客