KMP算法假定了解案件的原则,其实很easy。

KMP算法简述

关于根据自己的理解在这里。

KMP该算法由三个发明人的名称(Knuth、Morris、Pratt)的首字母组成,又称字符串查找算法。

个人认为能够理解为最小回溯算法,即匹配失效的时候,尽量少回溯。从而缩短时间复杂度。

KMP算法有两个关键的地方。1)求解next数组。2)利用next数组进行最小回溯。

1)求解next数组


next数组的取值仅仅与模式串有关。next数组用于失配时回溯使用。


在简单版本号的KMP算法中,每一个位置 j 的 next 值表示的是模式串的最长前缀的最后一个字符的位置(如果为 k ),当中最长前缀(长度为 k+1 )须要与模式串截至当前位置长度亦为 k+1 的后缀匹配。且 k 最大为 j-1 ,否则相当于没有回溯。

当k=-1的时候。表示找不到这种最长前缀。


用公式表示为



当k=-1的时候。表示空串。p表示模式串。



以下举一个计算next数组的样例,如果模式串是 “ abaabcaba ” 。


j 0 1 2 3 4 5 6 7 8
p a b a a b c a b a
next[j] -1 -1 0 0 1 -1 0 1 2

以 j = 8 为例,最长前缀为aba,最后一个字符位置为2,故 next[8] = 2 。



那么怎样高速求解next数组呢?

这里有点动态规划的思想在里面,当中位置 j 等于 0 的 next 值为-1,表示找不到这种最长前缀。 j > 0 时。next值能够通过 j - 1 位置的next值求得。

求解next[ j ]的步骤:
  1. t = next[ j - 1 ] + 1,t 指向可能等于 p[ j ] 的位置,即 p[ t ] 可能等于 p[ j ]。

  2. 假设 p[ t ]   =  p[ j ] , 那么 next[ j ] = next[ j - 1 ] + 1
  3. 假设 p[ t ]  !=  p[ j ] , 则令 t = next[ t - 1 ] + 1,继续第 2 步直到 t = 0 或者找到位置。

  4. 结束时推断p[ t ] 是否等于 p[ j ] ,假设等于则 next[ j ] = t , 否则等于 -1 。

下图表示了第一次不匹配。第二次匹配的过程,其他过程能够类推。当中     或     覆盖部分表示最长匹配串。   为待判定位置,   
为已判定位置。

0123                                                     j
×××××××××××××××××××××××××××××××××××××××××

×××××××××××××××××××××××××××××××××××××××××


2)利用next数组进行最小回溯


s ××××××××××××××××××××××××××××××××××××××××××××

p                                            ××××××××××××××

在j处不失配时,前面的有部分匹配。这时须要利用next数组信息进行最小回溯。

s ××××××××××××××××××××××××××××××××××××××××××××

p                                            ××××××××××××××

(这里 i 指向 s , j 指向 p。)

注意在 j = 0 的时候失配时。直接 i++ 就可以。

当 j > 0 的时候,须要利用next数组最快找到 p[ j ] == s[ i ] 的位置。

假设 j 移动到了0还找不到。则 i++,然后继续匹配。

这里我们能够发现仅仅有 j 回溯了,i没有回溯,可是因为普通版本号的 KMP 算法 j 须要不停地回溯直到找到合适的回溯位置,因此速度不是特别快。还能够继续优化。感兴趣的读者能够想想怎样事先求解好next数组从而不须要不停地回溯。

代码实现


strStr返回的是首次匹配的地址。假设不能匹配则返回NULL。

class Solution {
public:
vector<int> getNext(char* &s){
vector<int> next(strlen(s), -1); for(int i=1; i<strlen(s); i++){
int j = next[i-1]; /* 前一个字符的最长匹配长度 */ while(s[j+1] != s[i] && j>=0)
j = next[j]; if(s[j+1] == s[i])
next[i] = j+1;
// else 默觉得-1
} return next;
} char *strStr(char *haystack, char *needle) {
if(haystack==NULL || needle==NULL) return NULL;
if(strlen(haystack) < strlen(needle)) return NULL;
if(strlen(needle) == 0) return haystack; vector<int> next = getNext(needle);
int i = 0;
int j = 0;
int haystackLen = strlen(haystack);
int needleLen = strlen(needle);
while(i<haystackLen && j<needleLen){
if(haystack[i] == needle[j] ) {
i++;
j++;
if(j == needleLen) return haystack + i - j;
}else{
if(j == 0) i++;
else j = next[j-1]+1; /* 该步骤能够优化 */
}
} return NULL;
}
};

因为有人问有没有java版本号的,因为鄙人java比較挫。写java时部分还写成了scala的语法。不知道代码是否规范,有优化的地方还麻烦java方面的大神指点。

import java.util.*;

public class StrStrSolution {
private List<Integer> getNext(String p){
List<Integer> next = new ArrayList<Integer>();
next.add(-1); for(int i=1; i<p.length(); i++){
int j = next.get(i-1); while(p.charAt(j+1) != p.charAt(i) && j>=0)
j = next.get(j); if(p.charAt(j+1) == p.charAt(i))
next.add( j + 1 );
else
next.add( -1 );
} return next;
} public String strStr(String haystack, String needle) {
if (haystack == null || needle == null) return null;
if (needle.length() == 0) return haystack;
if (needle.length() > haystack.length()) return null; List<Integer> next = getNext(needle);
int i = 0;
int j = 0;
int haystackLen = haystack.length();
int needleLen = needle.length();
while(i < haystackLen && j < needleLen){
if(haystack.charAt(i) == needle.charAt(j) ) {
i++;
j++;
if(j == needleLen) return haystack.substring(i - j);
}else{
if(j==0) i++;
else j = next.get(j-1)+1;
}
} return null;
} public static void main(String[] args) {
String s = "babcabaabcacbac";
String p = "abaabcac";
StrStrSolution sol = new StrStrSolution();
System.out.println(sol.strStr(s,p));
}
}

【数据结构&amp;&amp;等差数列】KMP简介和算法的实现(c++ &amp;&amp; java)的更多相关文章

  1. hdu 3336:Count the string(数据结构,串,KMP算法)

    Count the string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  2. C++基础代码--20余种数据结构和算法的实现

    C++基础代码--20余种数据结构和算法的实现 过年了,闲来无事,翻阅起以前写的代码,无意间找到了大学时写的一套C++工具集,主要是关于数据结构和算法.以及语言层面的工具类.过去好几年了,现在几乎已经 ...

  3. Bug2算法的实现(RobotBASIC环境中仿真)

    移动机器人智能的一个重要标志就是自主导航,而实现机器人自主导航有个基本要求--避障.之前简单介绍过Bug避障算法,但仅仅了解大致理论而不亲自动手实现一遍很难有深刻的印象,只能说似懂非懂.我不是天才,不 ...

  4. 详解Linux内核红黑树算法的实现

    转自:https://blog.csdn.net/npy_lp/article/details/7420689 内核源码:linux-2.6.38.8.tar.bz2 关于二叉查找树的概念请参考博文& ...

  5. Python学习(三) 八大排序算法的实现(下)

    本文Python实现了插入排序.基数排序.希尔排序.冒泡排序.高速排序.直接选择排序.堆排序.归并排序的后面四种. 上篇:Python学习(三) 八大排序算法的实现(上) 1.高速排序 描写叙述 通过 ...

  6. Python八大算法的实现,插入排序、希尔排序、冒泡排序、快速排序、直接选择排序、堆排序、归并排序、基数排序。

    Python八大算法的实现,插入排序.希尔排序.冒泡排序.快速排序.直接选择排序.堆排序.归并排序.基数排序. 1.插入排序 描述 插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得 ...

  7. RMQ问题总结,标准RMQ算法的实现

    RMQ问题:对于长度为N的序列,询问区间[L,R]中的最值 RMQ问题的几种解法: 普通遍历查询,O(1)-O(N) 线段树,O(N)-O(logN) DP,O(NlogN)-O(1) RMQ标准算法 ...

  8. Canny边缘检测算法的实现

    图像边缘信息主要集中在高频段,通常说图像锐化或检测边缘,实质就是高频滤波.我们知道微分运算是求信号的变化率,具有加强高频分量的作用.在空域运算中来说,对图像的锐化就是计算微分.由于数字图像的离散信号, ...

  9. java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现

    java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...

随机推荐

  1. PHP从数据库获取的下拉树

    <?php include "config.php"; include "mysql.php"; $db = new Mysql('test'); //几 ...

  2. 链栈之C++实现

    链栈是借用单链表实现的栈.其不同于顺序栈之处在于: 1.链栈的空间是程序运行期间根据需要动态分配的,机器内存是它的上限.而顺序栈则是 静态分配内存的. 2.链栈动态分配内存的特性使得它一般无需考虑栈溢 ...

  3. TCP、UDP数据包大小的限制(UDP数据包一次发送多大为好)——数据帧的物理特性决定的,每层都有一个自己的数据头,层层递减

    1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...

  4. Citrix 服务器虚拟化之三十一 XenApp 6.5负载均衡

    Citrix 服务器虚拟化之三十一 XenApp 6.5负载均衡 说明:       环境基于实验三十 1.准备一台Windows Server 2008 R2的虚拟机名为XenAPP2,然后加入域k ...

  5. Api之Cors跨域以及其他跨域方式

    Web Api之Cors跨域以及其他跨域方式(三)   我们知道ajax不能跨域访问,但是有时我们确实需要跨域访问获取数据,所以JSONP就此诞生了,其本质使用的是Script标签,除JSONP以外还 ...

  6. zoj2314(有上下界的网络流)

    传送门:Reactor Cooling 题意:给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流 ...

  7. poj3281(最大流)

    传送门:Dining 题意:一些牛,一些食物,一些饮料,每头牛都有其喜欢的几种食物和几种饮料,求最多能给多少头牛即找到食物又找到饮料~也就是有多少个 牛---食物---饮料 的匹配,而且满足一一匹配, ...

  8. Gnu Linux下文件的字符编码及转换工具

    /*********************************************************************  * Author  : Samson  * Date   ...

  9. java nio-理解同步、异步,阻塞和非阻塞

    理解同步.异步,阻塞和非阻塞 结论:阻塞.非阻塞与是否同步异步无关.     转自知乎 “阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解, ...

  10. C# 计算字符串/文件的哈希值(MD5、SHA)

    原文 C# 计算字符串的哈希值(MD5.SHA) 已做修改 一.关于本文 本文中是一个类库,包括下面几个函数: /// 1)计算32位MD5码(大小写):Hash_MD5_32 /// 2)计算16位 ...