KMP算法的具体实现

来源:互联网 发布:建筑外观效果图软件 编辑:程序博客网 时间:2024/05/18 03:01

我打算重新开始学习数据结构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。(即回到模式串的头位置)

//-----本书中的字符串数组的第一个为数组长度 如下 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;    }
0 0
原创粉丝点击