zoukankan      html  css  js  c++  java
  • kpm字符串匹配算法

    首先是简单的朴素匹配算法

        /*
         * 返回子串t在主串s的位置,若不存在则返回0
         */
        public static int index(String s, String t) {
            
            int i = 0;//i记录主串当前位置的下标
            int j = 0;    //j记录子串当前位置的下标
            
            char[] c = t.toCharArray();
            char[] d = s.toCharArray();
            while(i < s.length() && j < t.length()) {
                if(c[i] == d[j]) {
                    i++;
                    j++;
                    if(j == t.length()) {
                        break;
                    }
                }else {
                    i = i - j;//如果不匹配i退回到上次匹配首位的下一位
                    j = 0;
                }
            }
            if(j == t.length()) {
                return i - j + 1;
            }
            return 0;
        }

    举例说明:

    s是 abcabcabd t是 abcabd,朴素的匹配算法每次发现不对都要重新回到上次匹配的首位,也就是要重新在s从找一次t的和第一个字符匹配的字符。

    但是像这个例子t字符串中一开始就有ab后面也有ab,也就是说如果匹配到最后一位发现不匹配的时候,就可以直接进行到这里

    所以这就是kmp改进的地方,先自行处理一下t字符串,找到t字符串中与前缀有重复的。建立一个next数组记录下这个重复,就比如这个t匹配到最后一个时发现s[5]和t[5]匹配失败,但是t[3]t[4]和t[1]t[2]是重复的,所以这里可以直接进行s[5]和t[3]的匹配。从代码的角度讲就是当i=5,j=5时,s[i]和t[j]匹配失败,那就修改j跳过之前与t字符串前缀重复的。

    这是一个next数组,这里j=0时没有前缀所以在匹配的时候就应该重t[0]开始匹配,j=1时前缀是a但是t[1]是b所以没有重复,所以还是0,一直到j=3,a和前缀a重复所以所以在匹配的时候就应该重t[1]开始匹配,到j=4,ab和前缀ab重复所以所以在匹配的时候就应该重t[2]开始匹配,j=5的时候abd和前缀abc并没有重复所以应该重t[0]开始匹配。

    再然后就是这个next数组怎么计算。。

    声明一个k初始化为0用于记录前缀被重复的长度了,就比如t[3]和t[0]重复就记1,此时next[]相应的位置就是1(k),t[3]t[4]和t[0]t[1]重复就记2,此时此时next[]相应的位置就是2(k),t[3]t[4]t[5]t[0]t[1]t[3不]重复就置0。

    整体代码:

    public static int KmpIndex(String s, String t) {
            int i = 0;
            int j = 0;    //j记录子串当前位置的下标
            int[] next = new int[t.length()];
            char[] c = t.toCharArray();
            char[] d = s.toCharArray();
            int k = 0;
            
            while(j < t.length()) {
                if(k == 0 && j == 0) {
                    //第一个字符的前后缀为空
                    next[j++] = 0;
                }else {
                    //使用k来记住已经和前者匹配到那个位置了
                    if(k == 0 ) {
                        //k=0 就是上一个未匹配
                        if(c[k] != c[j]) {
                            //不相等就是不匹配
                            next[j++] = 0;
                        }else {
                            //相等就是匹配,next[j]等于k+1,然后增加k和j的值
                            next[j++] = ++k;
                        }
                    }else {
                        //k!=0 就是上k个都已经匹配了
                        if(c[k] != c[j]) {
                            //不相等就是不匹配而且之后要重头匹配 k=0
                            next[j++] = 0;
                            k = 0;
                        }else {
                            //相等就是匹配,next[j]等于k+1,然后增加k和j的值
                            next[j++] = ++k;
                        }
                    }
                }
            }
            j = 0;
            while(i < s.length() && j < t.length()) {
                if(d[i] == c[j]) {
                    i++;
                    j++;
                    if(j == t.length()) {
                        break;
                    }
                }else {
                    j = next[j];
                }
            }
            if(j == t.length()) {
                return i - j;
            }
            return 0;
        }
  • 相关阅读:
    Qt 主窗口与子窗口之间传值
    Qt 如何使窗体初始最大化
    C++ strcmp与strncmp的比较
    Qt 状态栏(statusbar)的使用
    C++中的补零
    Qt QString转char[]数组
    PAT基础6-9
    PAT基础6-8
    PAT基础6-6
    PAT基础6-7
  • 原文地址:https://www.cnblogs.com/xxbbtt/p/7625726.html
Copyright © 2011-2022 走看看