经典算法之KMP算法、Sunday算法的java实现代码

经典的字符串匹配算法,KMP:非常经典,但比较难理解,且效率不是很高; Sunday:效率比kmp高很多,而且理解起来比较容易;

前言:继上次博文已经有半个月了,虽然没有粉丝,但我拖了自己的更。在此解释一下,因为前段时间家中奶奶去世,我请假回家了几天,然后回到武汉后又生病了,所以就一直没有更新博客。

正文:
1、KMP算法
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。
与暴力匹配相比:
暴力算法: 每次失配,S串的索引i定位的本次尝试匹配的第一个字符的后一个。P串的索引j定位到1;T(n)=O(n*m)
KMP算法: 每次失配,S串的索引i不动,P串的索引j定位到某个数。T(n)=O(n+m),时间效率明显提高

其实主要就是求这个next[j]数组
在这里插入图片描述
代码实现:

/**
     * 求一个字符数组的next数组
     * @param t 字符数组
     * @return
     */
    public static int[] getNextArray(char[] t){
        int[] next = new int[t.length];
        next[0] = -1;
        next[1] = 0;
        int k;
        for(int j = 2; j < t.length; j++){
            k = next[j-1];
            while( k!= -1){
                if(t[j-1] == t[k]){
                    next[j] = k + 1;
                    break;
                }else {
                    k = next[k];
                }
                next[j] = 0;
            }
        }
        return next;
    }

    /**
     * kmp实现
     * @param s 主字符串
     * @param t 模板字符串
     * @return
     */
    public static int kmpMatch(String s,String t){
        char[] s_arr = s.toCharArray();
        char[] t_arr = t.toCharArray();
        int[] next = getNextArray(t_arr);
        int i = 0,j = 0;
        while(i < s_arr.length && j < t_arr.length){
            if(j == -1 || s_arr[i]==t_arr[j]){
                i++;
                j++;
            }else
                j = next[j];
        }
        if(j == t_arr.length)
            return i-j;
        else
            return -1;
        }

    public static void main(String[] args) {
        System.out.println(kmpMatch("abcabaabaabcacb", "abaabcac"));
    }

虽然经典,奈何KMP难啃效率低。所以个人更推荐Sunday算法
2、Sunday算法

public static int contains(char[] str,char ch){
    for(int i=str.length-1;i>=0;i--){
         if(str[i]==ch){
             return i;
         }
     }
     return -1;
 }
    
public static void sunday(String s, String t) {
        char[] sarray = s.toCharArray();
        char[] parray = t.toCharArray();
        int slen = s.length();
        int plen = t.length();
        int i = 0, j = 0;
        while (i <= slen - plen + j) { //这句话控制索引i,j的范围
            if (sarray[i] != parray[j]) { //假如主串的sarry[i]与模式串的parray[j]不相等
                if (i == slen - plen + j) {
                    break;
                    /**假如主串的sarry[i]与模式串的parray[j]不相等,并且i=slen-plen+j,说明这已经
                     是在和主串中最后可能相等的字符段比较了,并且不相等,说明后面就再也没有相等的了,所以
                     跳出循环,结束匹配*/
                }
                //假如是主串的中间字段与模式串匹配,且结果不匹配,则就从模式串的最后面开始,
                //(注意是从后向前)向前遍历,找出模式串的后一位在对应的母串的字符是否在子串中存在
                int pos = contains(parray, sarray[i + plen - j]);
                if (pos == -1) {   //表示不存在
                    i = i + plen + 1 - j;
                    j = 0;
                } else {
                    i = i + plen - pos - j;
                    j = 0;
                }
            } else {  //假如主串的sarry[i]与模式串的parray[j]相等,则继续下面的操作
                if (j == plen - 1) {  //判断模式串的索引j是不是已经到达模式串的最后位置,
                    //j==plen-1证明在主串中已经找到一个模式串的位置,且目前主串尾部的索引为i,
                    //主串首部的索引为i-j,打印模式串匹配的第一个位置
                    System.out.println("the start pos is " + (i - j) + " the end pos is " + i);
                    //然后主串右移一个位置,再和模式串的首字符比较,从而寻找下一个匹配的位置
                    i = i - j + 1;
                    j = 0;
                } else {
                    //假如模式串的索引j!=plen-1,说明模式串还没有匹配完,则i++,j++继续匹配,
                    i++;
                    j++;
                }
            }
        }
    }

最后:说来惭愧,其实KMP我研究了好几天,到现在也不敢完全理解。而且更好的理解的话最好画一下图,我学习的时候也画了不少图,可惜没保存下来,所以就没贴出来。
如果有人像我一样觉得KMP太难理解的话,不要灰心,推荐直接把相对简单的Sunday算法研究好,最好能手撸一遍实现过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值