KMP算法的具体实现
2016-03-29 21:27
411 查看
我打算重新开始学习数据结构C语言版。今天看到的KMP算法。硬是把我绕进去了。主要是书上给的代码短小精悍,硬是看不懂怎么回事。
KMP算法的主要思想是即是利用部分匹配的思想,当匹配不同的时候,两个指针不用回溯到初始位置,对于主串中的指针就是不用回溯到开始匹配模式串的位置,对于模式串的指针就是不用回到模式串的头位置。主串中的指针无需回溯,而模式串中的指针则是根据相应next函数值,回溯到某个位置即可,这样算法时间复杂度就可以降低为O(n+m)。
核心部分就是求出模式串中的next函数值。
下例中数组均以 1 开始。
对于next[i] = j 来说,则说明 模式串中的前 ( j - 1 )个字符 和 从( i - 1)开始往前( j - 1 )个字符相匹配。定义next[1] = 0;此时
1. 若 t[i] = t[j] ,则说明next[ i + 1] = next[ i ] + 1。前 j 个字符都匹配。
2. 若 t[i] != t[j] ,则说明此时 对于第 ( i + 1) 个字符(t[i+1])来说,前面的 第 j 个字符匹配不成功。
此时应该缩小匹配长度,即令 j = next [ j ],如果此时相等的话,此时next [ i + 1] = next [ j ] + 1。如果不相等就进入循环。如果到最后都不相等则令next [ i + 1 ] = 1。(即回到模式串的头位置)
实在不知所云。于是我要先自己实现一遍,即便代码很丑…,再细细琢磨才行。
再来理解一下上面伟大的短代码:
下面我再把第一次写的C代码改善一下:
例题:(来自牛客网)
对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同。
给定两棵二叉树的头结点A和B,请返回一个bool值,代表A中是否存在一棵同构于B的子树。
思路:即对两个二叉树进行深度优先序列化,然后用KMP算法进行比较是否含有子串。
其实java里面很多算法都已经实现了,所以上述完全可以不用自己写kmp算法 ,而是直接用indexOf即可。如下:
KMP算法的主要思想是即是利用部分匹配的思想,当匹配不同的时候,两个指针不用回溯到初始位置,对于主串中的指针就是不用回溯到开始匹配模式串的位置,对于模式串的指针就是不用回到模式串的头位置。主串中的指针无需回溯,而模式串中的指针则是根据相应next函数值,回溯到某个位置即可,这样算法时间复杂度就可以降低为O(n+m)。
核心部分就是求出模式串中的next函数值。
下例中数组均以 1 开始。
对于next[i] = j 来说,则说明 模式串中的前 ( j - 1 )个字符 和 从( i - 1)开始往前( j - 1 )个字符相匹配。定义next[1] = 0;此时
1. 若 t[i] = t[j] ,则说明next[ i + 1] = next[ i ] + 1。前 j 个字符都匹配。
2. 若 t[i] != t[j] ,则说明此时 对于第 ( i + 1) 个字符(t[i+1])来说,前面的 第 j 个字符匹配不成功。
此时应该缩小匹配长度,即令 j = next [ j ],如果此时相等的话,此时next [ i + 1] = next [ j ] + 1。如果不相等就进入循环。如果到最后都不相等则令next [ i + 1 ] = 1。(即回到模式串的头位置)
//-----本书中的字符串数组的第一个为数组长度 如下 T的长度为 T[0]; //-----以下为类C语言的算法描述 void get_next(SString T,int next[]){ i =1; next[1] = 0; j = 0; while(i < T(0)){ if(j == 0 || T[i] == T[j]){++i;++j; next[i] = j;} else j = next[j]; }
实在不知所云。于是我要先自己实现一遍,即便代码很丑…,再细细琢磨才行。
//此处是C语言实现的。代码可以运行。 // int nt 为模式串的长度,char t[] 为模式串。 void get_next(char t[],int nt,int next[]){ int i = 1;//计数器:为当前匹配字母位置。( 0 开头的数组 ) next[0] = -1; next[1] = 0; int temp = next[i]; for(;i < nt;){ printf("next[% d]= %d\n",i,next[i]); if(t[i] == t[next[i]]){ next[i+1] = next[i] + 1; i++; temp = next[i]; printf("t[j]=t[k]:直接加一:t[i] + 1\n"); }else if(t[i] == t[temp]){ if( temp == 0){ printf("t[j] = t[0]:当和第一个字母匹配时,从第二个开始匹配:0\n"); next[i + 1] = 1; } else{ printf("t[j] = t[next..]:当和某个t[temp]匹配时:next[temp]+1\n"); next[i+1] = next[temp] + 1; } i++; temp = next[i]; }else if(temp < 0){ printf("temp < 0 :当无法匹配时,从第一个开始匹配:0 \n",temp); next[i+1] = 0; i++; temp = next[i]; }else { printf("进入循环,使得temp=next[temp]\n"); temp = next[temp]; } }
再来理解一下上面伟大的短代码:
void get_next(SString T,int next[]){ i = 1;//------for i = 1 to Tlength(T(0)-1),计算每个next[i]的值 next[1] = 0; j = 0;//j用来控制循环,j 始终等于 next[j],当符合条件时候,就 ++i ++j,然后赋值给next[i+1]。 while(i < T[0]){ if(j == 0 || T[i] == T[j]){ ++i; ++j; next[i] = j; //上面一行即等价于 next[i + 1] = j + 1;i++; //也许会更好理解一点。 } else j = next[j]; }
下面我再把第一次写的C代码改善一下:
//这里C语言实现,因为在算法中next[1]规定为0。在这里我可以规定 使得 next[0] = -1 //使其字符数组和next[]的计数都可以从0开始。免去一些麻烦。 void get_next(char t[],int nt ,int next[]){ int i = 0,j = -1; next[0] = -1; while( i < nt - 1){ if(j == -1 || t[i] == t[j]){ i++; j++; next[i] = j; } else j = next[j]; } }
例题:(来自牛客网)
对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同。
给定两棵二叉树的头结点A和B,请返回一个bool值,代表A中是否存在一棵同构于B的子树。
思路:即对两个二叉树进行深度优先序列化,然后用KMP算法进行比较是否含有子串。
import java.util.*; /* public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } }*/ public class IdenticalTree { public boolean chkIdentical(TreeNode A, TreeNode B) { // write code here String stra = toSerializale(A); String strb = toSerializale(B); char[] sa = stra.toCharArray(); char[] sb = strb.toCharArray(); return kmp(sa,sb) != -1; } public static String toSerializale(TreeNode head){ StringBuilder str = new StringBuilder(); if(head == null) return "#!"; str.append(head.val); str.append(toSerializale(head.left)); str.append(toSerializale(head.right)); return str.toString(); } public static int kmp(char[] a,char[] b){ int[] next = new int[b.length]; getNext(b,next); int i = 0,j = 0; while(i < a.length && j < b.length){ if(j == -1 || a[i] == b[j]) {i++;j++;} else j = next[j]; } if(j >= b.length) return i - b.length; return -1; } public static void getNext(char[] a,int[] next){ int i = 0; int j = -1; next[0] = -1; while(i < a.length - 1){ if(j == -1 || a[i] == a[j]) { i++; j++; next[i] = j;} else j = next[j]; } } }
其实java里面很多算法都已经实现了,所以上述完全可以不用自己写kmp算法 ,而是直接用indexOf即可。如下:
public boolean chkIdentical(TreeNode A, TreeNode B) { // write code here String stra = toSerializale(A); String strb = toSerializale(B); return stra.indexOf(strb) != -1; }
相关文章推荐
- c++复习要点总结之九——继承二
- zjnu 1783 PROSJEK(二分)
- linux系统基础及简单安装步骤
- java反射笔记
- Unity中简单使用Opengl
- binder第一课
- 随机验证码
- Problem_1015
- 深入理解 Java 虚拟机-javac 编译与 JIT 编译
- QProcess与外部程序的调用
- shell教程一 :介绍
- 类和对象
- 设计模式:外观模式(Facade)
- 91. Decode Ways
- 360搜索实习生(自然语言处理/机器学习/数据挖掘工程师)在线笔试知识点总结
- tune performance
- 在Xcode中使用Git进行源码版本控制
- 设计模式:外观模式(Facade)
- ArrayList以及数组的转换的思考
- Ubuntu安装Torch