常用算法——模式匹配(KMP)
来源:互联网 发布:vmware nat 端口转发 编辑:程序博客网 时间:2024/06/07 16:33
在一个字符串中查找一个子字符串有很多方法,最简单容易想到的算法便穷举,但这样的情况下算法复杂度为O(m * n)。
而KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度。
KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值越大,当然之前可能出现再匹配的机会就越大。
先来个图表来表示一下next数组的求解方式:
可是如何去求next数组每个数组元素的值呢?
-1. 逐个查找对称串
比如i = 0时,串对应的字符是a,不存在对称,所以next[0] = 0;
同样的,i = 1, 2, 3时 ag, agc, agct均不存在对称,所以next[1…3] = 0;
而i = 4时,agcta串的前缀a和后缀a相等,长度为1,所以next[4] = 1;
再比如i = 6时,agctagc的前缀agc和后缀agc相等,长度为3,所以next[6] = 3;
而i = 7时,agctagca只有前缀a和后缀a相等,长度为1,所以next[7] = 1;
之后,便可以次类推。
既然已经可以人工的推出这个next数组的值了,那么编程应该也可实现了:
编程思想如下:
1) 当前面字符的前一个字符的next值为0时,只要将当前字符与子串第一个字符进行比较。前面都是0,说明都不对称了,到当前位置时只是多加了一个字符,要对称的话最多是当前的和第一个对称。此时,如果当前字符和第一个相等,那个该位置的next值为1,否则为0;
2) 依此,可以总结一个规律,不仅当前一个字符的next值为0时,如果前面一个字符的next值是1,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明next就是2了;
3) 同2)中所说如果一直相等,就一直累加。
说到这里,上面情况理解起来应该没什么问题,现在的问题是如果遇到下一个不等,该怎么进行处理?
比如i = 14时,该位置的前一个字符的next值为7,但该位置的next值按照最大公共前后缀(agct)的规则来看,长度为4,其next的值为4。
在这里相当于重新寻找i = 14,更小的对称性,那么:
1)如果要存在对称性,那么对称程度肯定比前一个字符 的对称程度小,所以要找个更小的对称;
2)要找更小的对称,必然在对称内部还存在子对称,而且当前字符还必须紧接着在子对称串之后;
所以,agctagc 和 agctagc对称,agc、agc、agc、agc、对称。
算法实现与测试:
#include<iostream>#include <string>#include <map>#include <vector>#include <cmath>#include <bitset>#include <stack>using namespace std;//求next数组void create_next(const string &seq,int *next){ int k;//最大前后缀长度// int len = seq.size(); //字符串的第一个字符的最大前后缀长度为0 next[0] = 0; for (int i = 1, k = 0; i < len; ++ i) { //求出seq[0]···seq[q]的最大的相同的前后缀长度k while(k > 0 && seq[i] != seq[k]) k = next[k-1]; //如果相等,那么最大相同前后缀长度加1 if (seq[i] == seq[k]) { k++; } next[i] = k; }}//KMP算法实现int kmp(const string &str,const string &sub,int *next){ //当前已经匹配的串长度// int q; int n = str.size(); int m = sub.size(); create_next(sub,next); int i; for (i = 0, q = 0; i < n; ++ i) { while(q > 0 && sub[q] != str[i]) q = next[q-1]; if (sub[q] == str[i]) { q++; } if (q == m) { break; } } return i - m + 1;}int main() { string str, sub; cin >> str >> sub; int *next = new int[sub.size() + 1]; cout << "start position : " << kmp(str, sub, next) << endl; system("pause"); return 0;}
测试结果:
- 常用算法——模式匹配(KMP)
- 字符串模式匹配——KMP算法
- 字符串模式匹配——KMP算法
- 经典算法——KMP模式匹配
- 数据结构——串(朴素的模式匹配算法、KMP模式匹配算法)
- 数据结构——模式匹配(KMP算法)
- 模式匹配算法的改进——KMP算法
- 串模式匹配的改进算法——KMP算法
- 串的模式匹配算法——KMP算法解析
- 改进的模式匹配算法——KMP算法
- 模式匹配---KMP算法
- 模式匹配 KMP算法
- 模式匹配-KMP算法
- KMP模式匹配算法
- KMP模式匹配算法
- KMP模式匹配算法
- 模式匹配kmp算法
- 模式匹配算法kmp
- Http协议详解
- HD--1863畅通工程
- odoo9.0 如何将链接指向自定义form
- Java不定长度参数
- RSA密钥格式
- 常用算法——模式匹配(KMP)
- MySQL
- caffe 版本 yolo 过程记录
- 设计包含多种控件的用户登录界面(1)
- 饿了么移动APP的架构演进
- 很多开发都会用到的MVC、MVP、MVVM 架构
- Floyd-Warshall算法的应用——杭电OJ 1217 Arbitrage
- KMP之暴力匹配
- Android-Fragment生命周期