0
点赞
收藏
分享

微信扫一扫

KMP算法与暴力匹配法

才德的女子 2022-02-17 阅读 77

KMP详细讲解

部分匹配值

以字符串"ABCDABC"为例
前缀:A,AB,ABC,ABCD,ABCDA,ABCDAB
后缀:BCDABC,CDABC,DABC,ABC,BC,C
“A"的前缀和后缀都为空集.共有元素的长度位0
“AB"的前缀为"A”,后缀为"B”,共有元素的长度为0
“ABC"的前缀为"A”,“AB”,后缀"BC",“C”,共有元素的长度为0
“ABCD"的前缀为"A”,“AB”,“ABC”,后缀"BCD",“CD”,“D”,共有长度为0
“ABCDA"的前缀为"A”,“AB”,“ABC”,“ABCD”,后缀"BCDA",“CDA”,“DA”,“A”,共有元素为"A",长度为1
“ABCDAB"的前缀为"A”,“AB”,“ABC”,“ABCD”,“ABCDA”,后缀"BCDAB",“CDAB”,“DAB”,“AB”,“B”,共有元素为"AB",长度为2
因此对于字符串ABCDABC它的部分匹配表为:
A B C D A B D
0 0 0 0 1 2 0

KMP的next数组

KMP的next数组讲解

KMP完整代码

package algorithm.DataStruct.Frequentlyused.KMP;

/**
 * @author: Serendipity
 * Date: 2022/2/15 21:24
 * Description:
 * KMP方法算法就利用之前判断过信息,通过一个nexit数组,保在模式串中前后最长公共子序列的长度,
 * 每次回溯时,通过next数组找到,前面匹配过的位置,省去了大量的计算时间
 */
public class KMP {
    public static void main(String[] args) {
        String str = "尚硅谷你三个功能爱上啊实打实带宽理解";
        String str1 = "打实带";
        System.out.println(getIndexOf(str,str1));
    }
    public static int getIndexOf(String str,String substr){
        int[] next = kmpNext(substr);
        return getIndexOf(str,substr,next);
    }
    public static int getIndexOf(String str,String substr,int[]next){
        for(int i=0,j=0;i<str.length();i++){
            while(j>0 && str.charAt(i)!=substr.charAt(j)){
                j=next[j-1];
            }
            if(str.charAt(i)==substr.charAt(j)){
                j++;
            }
            if(j==substr.length()){
                return i-j+1; //这里要+1是因为退出的时候i还没有++
            }
        }
        return -1;
    }
    /**
     * 该函数用于获取字符串的部分匹配表next
     * @param substr 待获取部分匹配表的字符串
     * @return 返回该字符串的部分匹配表
     * 理解:next[0]直接设置为0即可
     * next[k]=x代表的是从当前这个字符开始有x个字符与从头开始的x字符是一样的
     * abcabc->0 0 0 1 2 3 k=3时x=1 代表str.charAt(3)(当前字符)==str.charAt(0)(从头开始的x个字符);
     * KMP算法可以理解为看门牌算法 因为当next[k]>0的时候 可以得到
     * str.charAt(k)(当前字符)==str.charAt(next[k]-1)(从头开始的x个字符);
     * k=4时x=2 那么代表str.charAt(4)(当前字符'b')==str.charAt(next[4]-1)('b')(从头开始的x个字符);
     *      str.charAt(3)(当前字符'a')==str.charAt(next[3]-1)('a')(从头开始的x个字符);
     * 因此再我们要求next[k+1](比如next[5])的时候,由于我们肯定知道next[k](next[4]=2),因此代表str.charAt(k+1)如果
     * 等于str.charAt(next[k]=2)(注:此处k=4),那么就说明next[k+1]=3,如果不等,例如为a b c a b d,也就是
     * str.charAt(k+1)!=str.charAt(next[k]=2),那么此时k=next[next[k]=2]=0;此时判断str.charAt(0)==str.charAt(5)
     * 不相等因此令next[5]=0;
     */
    public static int[]kmpNext(String substr){
        int[]next = new int[substr.length()];
        next[0]=0;
        //i是字符串后缀 j是字符串前缀,同时也是最长字串前后缀相等的长度
        for(int i=1,j=0;i<substr.length();i++){
            //不相等的时候就一直找知道找到相等的
            while(j>0&&substr.charAt(i)!=substr.charAt(j)){
                j=next[j-1];
            }//相等就++
            if(substr.charAt(i)==substr.charAt(j)){
                j++;
            }
            next[i]=j;//然后赋值
        }
        return next;
    }

    /**
     * kmp的next数组的第二种得到方法
     * next[k]的值代表的是前面的next[k]个字符与后面的next[k]个字符
     * 这里的前面和后面指的是next[k]他对应的字符对应的前面的字符和后面的字符
     * 例如next[k]=3,那么说明这个next[k]对应的前面三个字符和从头开始的三个字符一定相等
     * 与上面那种只有一点点差异
     * @param str
     * @return
     */
    public static int[]getKMPNext(String str){
        int[]next=new int[str.length()];
        next[0]=str.length();
        next[1]=0;
        int i=1,j=0;
        while(i<next[0]){
            if(j==0||str.charAt(i)==str.charAt(j)){
                next[++i]=++j;
            }else{
                j=next[j];
            }
        }
        return next;
    }
    public static int kmp(String str,String substr){
        return -1;
    }
    /**
     * 暴力匹配法
     *
     * @param str    待匹配字符串
     * @param substr 字串
     * @return -1代表没有字串 否则返回字串第一个字符再字符串中的位置
     */
    public static int violentMatch(String str, String substr) {
        char[] chars = str.toCharArray();
        char[] subchars = substr.toCharArray();
        int i = 0, j = 0;
        while (i < chars.length && j < subchars.length) {
            if (chars[i] == subchars[j]) {
                i++;
                j++;
            } else {
                i = i - (j - 1);//i每次总是增加和j一样的数量
                j = 0; //因此如果这次匹配失败那么i只需要下移1位即可
            }   //因此还需要减掉和j一样的前进步数
        }
        if (j == subchars.length) {
            return i - j;//这次找到了 那么只需要将i-j的长度即可得到这个字符首次出现的位置
        } else {
            return -1;
        }

    }
}

举报

相关推荐

0 条评论