算法学习——KMP算法

KMP算法

字符串匹配算法

时间复杂度:O(n)

难点:生成next[]数组,还有理解为什么这样匹配是正确的

生成next数组:自己动手举个例子会好理解

为什么这样匹配是正确且避免回溯:若str1和str2已经匹配了一段,则str2是返回其最大前缀的后一个字符的位置(即next[x]),此时str1前面的字符和str2的最大前缀还是匹配上的,这就避免了回溯了。

详细讲解可参考blog:https://blog.csdn.net/v_july_v/article/details/7041827

(这个blog可以看懂为什么要这样匹配,算法的写法,但是next数组的生成还是要自己举例子才更直观)

/**
 * KMP 求str1中匹配到的str2的起始位置
 * O(n)
 */
public class KMP {


    public static void main(String[] args){
        String str1 = "abcd1234546edgeabc";
        String str2 = "12345";
        System.out.println(new KMP().getIndexOf(str1,str2));
    }


    /**
     * KMP主函数
     * @param str1 需要搜素字符串
     * @param str2 被匹配的字符串
     * @return
     */
    public int getIndexOf(String str1,String str2){
        if(str1 == null || str2 == null || str2.length() < 1 || str1.length() < str2.length()){
            return -1;
        }

        int index1 = 0;
        int index2 = 0;
        int[] next = getNextArr(str2);
        while(index1 < str1.length() && index2 < str2.length()){
            // 两个字符匹配,那么就一起继续往下匹配
            if(str1.charAt(index1) == str2.charAt(index2)){
                index1++;
                index2++;
                // 如果此时两个字符不匹配,且此时index2=0,那就让str1的下一个字符继续与str2匹配
            }else if(next[index2] == -1){
                index1++;
                // 如果两个字符不匹配,且index2也不是等于0(即str1和str2匹配了一段),此时要把index2挪动到它的最大前缀的后一个字符,
                // 再继续跟index1所指向的字符匹配
            }else{
                index2 = next[index2];
            }
        }
        return index2 == str2.length() ? index1 - index2 : -1;
    }


    /**
     * 求next数组
     * next数组:str对应的index位置的0-index-1的最大匹配前后缀的长度,例如abcd1234cdabcd的最大匹配前后缀是4(abcd)
     * @param str
     * @return
     */
    public int[] getNextArr(String str){
        if(str.length() == 1){
            return new int[]{-1};
        }
        int[] next = new int[str.length()];
        // 人为规定next[0] = -1;next[1] = 0(因为其没有前缀嘛,然后前后缀又不能是同一个字符)
        next[0] = -1;
        next[1] = 0;
        // 跳到的地方的index(这个很难解释,看视频吧或者找KMPblog吧)
        int cn = 0;
        int index = 2;
        while(index < next.length){
            //如果相等,那说明求出来了
            if(str.charAt(cn) == str.charAt(index-1)){
                next[index++] = ++cn;
                // 否则让它将继续往前跳
            }else if(cn > 0){
                cn = next[cn];
            }else {
                // 如果cn < 0,说明cn已经跳到0的位置了,index位置的最大前后缀为0
                next[index++] = 0;
            }
        }
        return next;
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值