KMP算法

KMP算法

主要用于查找定位,在一个字符串中匹配另一个字符串,一个长字符串中找一个短字符串,是不是存在,存在的位置,如果使用暴力查找就是双层循环,时间复杂度m*n
那么KMP的思想呢,就是在简化查找的复杂度,KMP也成为模式匹配,KMP是怎么做的呢,就是通过先对短字符串进行计算,得到当字符串长度下,前缀和后缀相同的个数,
记录为next数组,这样当匹配不到的时候,长字符的下标就不回退,去回退短字符串的下标,那么应该回退多少呢,这就跟next数组有关系了

普通比较算法

简单写一下吧,主要思想就是遍历长串,然后对比长串和短串的比较字符,如果遍历到结尾,如果短串遍历完成都相等,那么就是包含,开始的长串的下标就是开始的值i,
从i开始包含,如果中间遇到不相等的,就把i归0,然后i向后走一位,继续这样遍历时间复杂度,就是两者的长度的乘积。双层循环得到结果

class Solution{
    public int indexOf(String content,String con){
        // 判断content里面是不是包含字符串con
        int length = content.length();
        int len = con.length();
        boolean notContains;
        for (int i = 0; i < length-len+1;i++) {
            notContains = false;
            for (int j = 0; j < len; j++) {
                if(content.charAt(i+j)!=con.charAt(j)){
                    notContains = true;
                    break;
                }
            }
            if(!notContains){
                return i;
            }
            
        }
        return -1;
    }
}

next数组是什么,怎么计算的

比如长短字符串 abdacfgaca 中查找 abdab 那么先对 短字符串求next数组,即先遍历短字符串,从1开始,
即第一次 ab 这时候next即为 [0,0]
第二次 字符串 abd 这时候next为 [0,0,0],
然后第三次 abda 这时候next [0,0,0,1] 这时候为1,也就是从前开始a,等于从后面开始截取的也是a,
第四次 abdab 这时候next [0,0,0,1,2] 这个为2代表的这个字符串,从前面截取两个字符,等于从后面截取两个字符

这里就是当 遍历到abdab 发现b不想等,那么就退回到前一个a,即 next[j-1],这里j代表的是b的下标,a的下标就是j-1,然后next[j-1] 就是找到的前面的a的位置后面的,
开始对比,也就是不采用当前a,找公共前缀的地方,尽量多的共用之前遍历过的,不进行重复遍历

具体怎么进行计算next的值呢,怎么知道的前几个等于后几个的,下面看下next怎么计算的

class Solution{
    public int[] getNext(String content){
        int length = content.length();
        int[] next = new int[length];
        //i 用来记录遍历字符串的下标,从1开始,因为单个字符无前后缀,没有多个相同的问题
        // i代表的是后缀开始的位置
        int i=1;
        // j代表的就是前缀遍历到的位置,也就是当前前后字符串已经相等的数量
        int j=0;
        // 遍历的字符串
        while (i<content.length()){
            if(content.charAt(i)==content.charAt(j)){
                // 相同的时候,即next的值为当前j的值+1,j代表的是之前的几个相等的,加上当前的就是+1(当前的这个也相等了)
                next[i] = j+1;
                // 同时后移,注意这个i后移的时机,i不会回退,一种是跟j相等的时候,一种就是下面,跟j完全就没相等的位置,等于从i从新开始匹配
                i++;
                j++;
            }else {
                // 遇到不想等的时候要回退,那么应该回退多少呢,
                if(j!=0){
                    // 为什么是next[j-1] 因为这时候 i 和j位置的值不想等,就需要找j里面有多少前缀是等于,利用前面的相等的部分,继续向后遍历
                    j=next[j-1];
                }else {
                    // 这时候j为0的时候当然不需要回退了,直接+i的值就行,next对应的值为0,也不需要进行赋值了
                    i++;
                }
            }
        }
        return next;
    }
    public int getIndexByKMP(String longContent,String shortContent){
        int[] next = getNext(shortContent);
        int lengthLong = longContent.length();
        int lengthShort = shortContent.length();
        int i=0;
        int j=0;
        // 这里的遍历跟获取next有些类似,i
        while (i<lengthLong&&j<lengthShort){
            // 如果相等时候直接都进行前进
            if(longContent.charAt(i)==shortContent.charAt(j)){
                i++;
                j++;
            }else {
                // 不相等,需要回退j,回退多少取决于,当前字符前一个字符之前出现的位置在哪,回退到它后面,
                // 比如 abcabdc 遍历到d发现不相等了,这时候应该回退到d的前一位即b 前一次出现的位置,即下标1的位置,开始比较下标2的位置是不是相等
                if(j!=0){
                    j=next[j-1];
                }else {
                    // 这里代表的当前就是0,跟长串也不想等,没有共同点,那就直接继续遍历长串
                    i++;
                }
            }
        }
        // 最后判断相等就是为了看是不是存在,是遍历完长串还是遍历完短串结束的循环
        if(j==lengthShort){
            return i-lengthShort+1;
        }
        return -1;
    }
}

总结

可能表达有些简陋,还是总结下,整体思想就是怎么简化查找字符串是不是存在的问题,减少遍历,即加了一个额外的数组,来存前面的共同的前缀的,跳到前一个匹配的完全匹配的位置继续匹配,
达到了不需要回退长字符串,减少回退短字符串的效果,最主要的就是next的生成,怎么找到前一个共同前缀的位置,后面进行对比的思想是类似的

你可能感兴趣的:(JAVA,算法,java,数据结构,kmp,字符串查找)