字符串的匹配(暴力匹配,KMP)

暴力匹配法

如果是用暴力匹配的思路,并假设现在str1匹配到i位置,子串str2匹配到j位置,则有:

  1. 入股当前字符匹配成功(即str1[i] == str2[j]),则i++, j++,继续匹配下一个字符
  2. 如果失配(即str1[i] != str2[j]),令i= i - (j - 1), j = 0; 相当于每次匹配失败时,i回溯,j被置为0
  3. 用暴力方法解决的话就会有大量的回溯,每次只移动一位,若是不匹配,移动到下一位接着判断,浪费大量时间
  4. 暴力匹配算法实现
package KMP;
// 暴力匹配算法实现
public class ViolenceMatch {
    public static void main(String[] args) {
        String str1 = "jaklsdjaowijdlkaj";
        String str2 = "jao";
        System.out.println(violenceMatch(str1, str2));
    }
    // 暴力匹配算法
    public static int violenceMatch(String str1, String str2){
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();
        int s1Len = s1.length;
        int s2Len = s2.length;
        int i = 0, j = 0;
        while (i < s1Len && j < s2Len){
            if (s1[i] == s2[j]) {
                i++;
                j++;
            } else { // 没有匹配成功
                // 如果失败 ,将i回溯向前移动一位,将j重新置为0
                i = i - (j - 1);
                j = 0;
            }
        }
        if(j == s2Len){
            return i-j;
        }else {
            return  -1;
        }

    }
}

KMP

KMP方法算法就利用之前判断过信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置省去了大量的计算时间

KMP的关键就是查找重复的子串,寻找公共子序列,拿“ABCDABD”来举例子:

首先我们获取A,它的前缀没有,后缀没有那么,自然没有重复公共子序列

之后我们又读取了一个B,与之前的A形成了“AB”字符串,它的前缀有{“A”},后缀有{“B"},很明显前缀和后缀之间没有重复的,所以也是没有重复公共子序列

之后我们读取了一个C,与之前的“AB”形成“ABC”字符串,它的前缀有{“A”,“AB”},后缀有{“C”,“BC”},很明显前缀和后缀都没有重复的,所以没有重复公共子序列

继续往下读,读取到一个D,与之前的“ABC”形成“ABCD”,它的前缀有{“A”,“AB”,“ABC”},后缀有{“D”,“CD”,“BCD”},很明显前缀和后缀也没有重复的,所以没有重复公共子序列

接下来我们继续向下获取,得到一个A,形成了“ABCDA”字符串,它的前缀有{“A”,“AB”,“ABC”,“ABCD”},后缀有{“A”,“DA”,“CDA”,“BCDA”},这时我们看到前缀和后缀之间都有一个“A”,这个A就是它们之间重复公共子序列,我们在当前位置标记一个1

接下来继续读取,得到B,组成“ABCDAB”,前缀是{“A”,“AB”,“ABC”,“ABCD”,“ABCDA”},后缀有{“B”,“AB”,“DAB”,CDAB",“BCDA”},我们发现它的前缀和后缀存在公共子序列,就是“AB”,那么我们将当前位置标记为2(因为“AB”长度是2)

接下来继续读取,得到D,组成“ABCDABD”,前缀是{“A”,“AB”,“ABC”,“ABCD”,“ABCDA”,“ABCDAB”},后缀有{“D”,“BD”,“ABD”,“DABD”,CDABD",“BCDAD”},我们发现它的前缀和后缀不存在公共子序列

就此我们得到了一个表:在这里插入图片描述

package KMP;

import java.util.Arrays;

// KMP
public class KMPAlgorithm {
    public static void main(String[] args) {
        String str1 = "BBC ABCDAB ABCDABCDABDE" ;
        String str2 = "ABCDABD";

        int[] next = kmpNext(str2);
        System.out.println(Arrays.toString(next));
        int index = kmpSearch(str1, str2, next);
        System.out.println(index);
    }

    // 获取到一个字符串(子串)的部分匹配值
    public static int[] kmpNext(String dest){
        // 创建一个next数组,保存部分匹配值
        int[] next = new int[dest.length()];
        next[0] = 0; // 如果dest只有一个字符,那部分匹配值就是0
        for (int i = 1, j = 0; i < dest.length() ;i++){
            while (j > 0 && dest.charAt(i) != dest.charAt(j)){
                // 我们需要从next[j-1]获取新的j
                // 直到我们发现有满足时才推出
                j = next[j - 1];
            }
            // 当这个条件满足时,部分匹配值就是 +1
            if (dest.charAt(i) == dest.charAt(j)){
                j++;
            }
            next[i] = j;
        }
        return next;
    }
    // 搜索算法
    public  static  int kmpSearch(String str1, String str2, int[] next){
        for (int i = 0, j = 0; i < str1.length(); i++) {
            // 需要考虑当两个不相等的时候
            if(j > 0 && str1.charAt(i) != str2.charAt(j)){
                j = next[j - 1];
            }
            if(str1.charAt(i) == str2.charAt(j)){
                j++;
            }
            if(j == str2.length()){
                return i - j + 1;
            }
        }
        return -1;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值