美文网首页
在母串内查找子串 KMP 算法

在母串内查找子串 KMP 算法

作者: 是瑞瀛呀 | 来源:发表于2020-05-24 01:56 被阅读0次

    之前了解了下这个算法,实现了下,有问题欢迎指教

    package org.huangry.colorful.common.utils;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Arrays;
    
    /**
     * ClassName: KMPUtil
     *
     * @author huangruiying
     * @version 1.0
     */
    public class KMPUtil {
    
        private Logger logger = LoggerFactory.getLogger(KMPUtil.class);
    
        //计算后的序列
        private static int[] substring_sequence_arr;
    
        private String parentStr = "";
        private String childStr = "";
    
        public KMPUtil(String parentStr, String childStr){
            this.parentStr = parentStr;
            this.childStr = childStr;
            //处理子串:生成子串序列到substring_sequence_arr
            generate_substring_sequence(childStr);
            logger.info(">>> KMP init success");
            logger.info(">>> substr_seq_array {}",Arrays.toString(substring_sequence_arr));
        }
    
        /**
         * 查询母串内是否包含子串
         * 然后返回索引
         *  不包含时 索引为 -1
         *  包含时 索引为第一次出现的位置
         */
        public int start(){
            //通过KMP查询
            int index = search(parentStr,childStr);
            logger.info("子串在目标字符串的索引: {}", index);
            return index;
        }
    
        /**
         * 获取母串内包含子串的个数
         * @return
         */
        public int getCount(){
            return count(parentStr,childStr);
        }
    
        /**
         * KMP查询
         * @author huangruiying
         * @param target 目标字符串
         * @param substring 待查询子串
         * @return 查找到的子串在目标字符串中的起始索引
         */
        private int search(final String target, final String substring) {
    
            final char[] target_arr = target.toCharArray();
            final char[] substring_arr = substring.toCharArray();
            //初始化游标 i:target游标 j:子串游标
            int i=0,j=0;
    
            //边界
            while (i < target_arr.length){
                char target_val = target_arr[i];
                char substring_val = substring_arr[j];
                if(target_val == substring_val){
                    //跳出条件 (-1 : 长度跟索引比较,所以要-1)
                    if(j == (substring.length()-1)){
                        /**
                         * 返回起始索引
                         * target&index:sssa&0123
                         * substr&index:sa&01
                         * 返回 (3-1)= 2 , 2为substring sa第一字在target中出现的索引位置。
                         */
                        return i-j;
                    }
                    i++;
                    j++;
                }else{
                    int before_j = j - 1;
                    //子串边界
                    if(before_j < 0){
                        j = 0;
                    }else{
                        j = substring_sequence_arr[before_j];
                    }
                    //未寻找到时的跳出条件
                    if(i == (target.length()-1)){
                        return -1;
                    }
                }
            }
    
            return -1;
        }
    
        private int count(final String target, final String substring) {
    
            int count = 0;// 计数 target 内包含多少个 substring
    
            final char[] target_arr = target.toCharArray();
            final char[] substring_arr = substring.toCharArray();
            //初始化游标 i:target游标 j:子串游标
            int i=0,j=0;
    
            //边界
            while (i < target_arr.length){
                char target_val = target_arr[i];
                char substring_val = substring_arr[j];
                if(target_val == substring_val){
                    if(j == (substring.length()-1)){
                        count ++ ;
                        i ++ ;
                        j = 0;
                        continue;
                    }
                    i++;
                    j++;
                }else{
                    int before_j = j - 1;
                    //子串边界
                    if(before_j < 0){
                        j = 0;
                    }else{
                        j = substring_sequence_arr[before_j];
                    }
                    //未寻找到时的跳出条件
                    if(i == (target.length()-1)){
                        return count;
                    }
                    i ++ ;
                }
            }
            return count;
        }
    
        /**
         * 生成子串序列
         * 子串:abcdabca
         * 序列:00001231
         * 规则:使用i,j两游标,
         * 初始化变量i=0,j=1
         * 初始化序列数组 0 0 0 0 0 0 0 0
         * 对比i与j所在索引处的数组值是否相等
         * 若相等,那么j处序列数组值为i+1(索引+1) ,然后i,j分别向后移动一位
         * 若不相等,查看序列数组内当前i-1位置的值(索引),并将该值赋值给当前变量i 结束本次循环(注意i的边界情况)
         * @author huangruiying
         * @param substring 子串
         */
        private void generate_substring_sequence(String substring){
            substring_sequence_arr = new int[substring.length()];
            char[] substring_arr = substring.toCharArray();
            //游标
            int i=0,j=1;
            while (j<substring_arr.length){
                char iChar = substring_arr[i];
                char jChar = substring_arr[j];
                if(iChar == jChar){
                    substring_sequence_arr[j] = i+1;
                    j++;
                    i++;
                }else{
                    int before_i = i-1;
                    //边界
                    if(before_i < 0){
                        substring_sequence_arr[j] = 0;
                        j++;
                    }else{
                        //通过序列内当前i的前一个值,改变i游标
                        i = KMPUtil.substring_sequence_arr[before_i];
                    }
                }
            }
        }
    
    
    }
    
    
    
    

    相关文章

      网友评论

          本文标题:在母串内查找子串 KMP 算法

          本文链接:https://www.haomeiwen.com/subject/qxzrahtx.html