字符串匹配算法BF、KMP、BM、KR算法代码汇总

最近在学字符串匹配算法,也算搞懂了一点点,在这里将我写的代码汇总一下。
这里的代码被我改成了LeetCode的题目格式,读者有兴趣的话可以去过一下这道题。
leetcode28. 实现 strStr()

class Solution {
    
    public int strStr(String haystack, String needle) {
        if(haystack == null || needle == null) {
            return -1;
        }
        if(needle.isEmpty()) return 0;
        if(haystack.isEmpty()) return -1;
        return KR_Match(haystack, needle);
    }

    //KMP算法
    int KMP_Match(String T, String P) {
        int n = T.length(), i = 0;
        int m = P.length(), j = 0;
        
        //构建next表
        int[] next = new int[m];
        int t = next[0] = -1;
        while(j < m - 1) {
            if(t < 0 || P.charAt(j) == P.charAt(t)) {
                t++;
                j++;
                next[j] = P.charAt(j) == P.charAt(t) ? next[t] : t;
            } else {
                t = next[t];
            }
        }       
        
        //开始进行匹配
        j = 0;
        while(i < n && j < m) {
            if(j < 0 || T.charAt(i) == P.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        
        //若i - j > n - m则代表找不到对应的子串
        return i - j > n - m ? -1 : i - j;
    }


    //BF算法
    int BF_Match(String T, String P) {
        int n = T.length(), i = 0;
        int m = P.length(), j = 0;
        while(i < n && j < m) {
            if(T.charAt(i) == P.charAt(j)) {
                i++;
                j++;
            } else {
                i -= (j - 1);
                j = 0;
            }
        }
        return i - j > n - m ? -1 : i - j;
    }
    
    //BM算法
    int BM_Match(String T, String P) {
        int n = T.length();
        int m = P.length();

        //构造BC表
        int[] BC = new int[256];
        for(int i = 0; i < 256; i++) {
            BC[i] = m;
        }
        for(int i = 0; i < m - 1; i++) {
            BC[P.charAt(i)] = m - i - 1;
        }

        //构造suffix表
        int[] suffix = new int[m];
        suffix[m - 1] = m;
        for(int i = m - 2; i >= 0; i--) {
            int q = i;
            while(q >= 0 && P.charAt(q) == P.charAt(m - 1 - i + q)) {
                --q;
            }
            suffix[i] = i - q;
        }

        //构造GS表
        int[] GS = new int[m];
        for(int i = 0; i < m; i++) {
            GS[i] = m;
        }
        int j = 0;
        for(int i = m - 1; i >= 0; i--) {
            if(suffix[i] == i + 1) {
                for(; j < m - 1 - i; j++) {
                    GS[j] = m - 1 - i;
                }
            }
        }
        for(int i = 0; i < m - 1; i++) {
            GS[m - 1 - suffix[i]] = m - 1 - i;
        }

        //开始匹配
        int i = 0;
        j = 0;
        while(j <= n - m) {
            for(i = m - 1; i >= 0 && P.charAt(i) == T.charAt(i + j); i--);
            if(i < 0) return j;//i < 0代表匹配完毕,也就是找到了,可以直接返回j
            else j += Math.max(GS[i], BC[T.charAt(i + j)] - m + 1 + i);
        }
        return -1;
    }

    //Karp-Rabin算法
    int KR_Match(String T, String P) {
        int n = T.length();
        int m = P.length();
        int MOD = 1000000;

        int power = 1;
        for(int i = 0; i < m; i++) {
            power = (power * 31) % MOD;
        }

        //计算出模式串的哈希值,31是个经验值,更不容易冲突
        int HashP = 0;
        for(int i = 0; i < m; i++) {
            HashP = (HashP * 31 + P.charAt(i)) % MOD; 
        }

        int HashT = 0;
        for(int i = 0; i < n; i++) {
            //计算文本串中与模式串长度相同的子串的哈希值
            HashT = (HashT * 31 + T.charAt(i)) % MOD;

            //当i < m - 1时,即第一个子串长度小于m时,让其继续计算
            if(i < m - 1) {
                continue;
            }

            //当子串长度超出m时,丢掉最前面的字符的哈希值
            if(i >= m) {
                HashT = (HashT - T.charAt(i - m) * power) % MOD;
                if(HashT < 0) { //丢掉最前面的哈希值之后,可能变为负数,这时直接加上MOD就行了
                    HashT += MOD;
                }
            }

            //两个串的哈希值相等
            if(HashT == HashP){
                //但是两个串并不一定完全相等,可能存在冲突,通过equals方法再比较
                if(P.equals(T.substring(i - m + 1, i + 1))) {
                    return i - m + 1;
                }
            }
        }

        return -1;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值