KMP算法的具体实现

我打算重新开始学习数据结构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;
    }

你可能感兴趣的:(数据结构与算法,C程序设计教程)