【面试刷题】字符串匹配Robin Karp算法

【面试刷题】字符串匹配Robin Karp算法

一、题目LeetCode-28.实现strStr()

image-20220112192807716

image-20220112192832894

二、实现O(n^2)的普通算法

class Solution {
    public int strStr(String haystack, String needle) {
        //进行异常判断
        if (haystack==null||needle==null)
            return 0;
        
        for (int i = 0; i<haystack.length() - needle.length() + 1;i++){
            int flag = 1;
            for (int j = i; j<i+needle.length(); j++){
                if (haystack.charAt(j) != needle.charAt(j-i)){
                    flag = 0;
                    break;
                }  
            }
            if (flag == 1)
                return i;
        }

        return -1;
    }
}

三、Robin Karp算法

在字符串比较的时候,需要向上面的普通方法,一个一个字母去匹配比较。

在数字比较的时候,只需要比较数字的值即可。

那么,如何把字符串的比较变为数字的比较呢?----hashCode

1、编写HashFunction()基础

可以通过字符,和一个数的乘积来转换为一个数字

image-20220112195008370

比如abcd,拆分成a、b、c、d四个char类型,然后分别乘以对于的底数最后相加,就是这个字符串的Hash值。

有可能会超出整数的范围,那么可以对出来的值取模。

例:
"abcde"
= (a * 31^4 ) + (b * 31^3 ) + (c * 31^2 ) + (d * 31^1 ) + (e * 31^0 ) % 10^6

在此基础之上,如果要得到"bcde"的hash值怎么办?
模运算,用"abcde"的hash值 减去 (a * 31^4 )得到的值,再次模10^6即可

如果计算出来的hash值相同,那么可能两个字符串是相同的,还需要进一步判断。

如果计算出来的hash值不相同,那么两个字符串一定是不相同的。

效率取决于会有多少hash冲突。整个的时间复杂度是O(n + m)次,最后的匹配是m次。

2、实现Robin Karp算法

class Solution {
    public int BASE = 1000000;
    public int strStr(String source, String target) {
        if (source == null || target == null){
            return 0;
        }
        
        int m = target.length()
        if (m.length() == 0){
            return 0;
        }
        
        // 31 ^ m
        int power = 1;
        for (int i = 0; i < m; i++){
            power = (power * 31) % BASE;
        }
        
        //计算targetCode
        int targetCode = 0;
        for (int i = 0; i < m; i++){
            targetCode = (targetCode * 31 + target.charAt(i)) % BASE;
        }
        
        int hashCode = 0;
        for (int i = 0; i < source.length(); i++){
            //abc + d
            hashCode = (hashCode * 31 + source.charAt(i)) % BASE;
            //所计算的hashCode字符串长度小于目标的长度
            if (i < m-1){
                continue;
            }
            
            //abcd - a,所计算的hashCode字符串大于等于目标的长度,如i=2,m=2实际上已经计算了三个字符。
            if (i >= m){
                //减掉第一个字符后的hashCode,例如source.charAt(2 - 2)
                hashCode = hashCode - (source.charAt(i - m) * power)%BASE;
                if (hashCode < 0){//变为整数
                    hashCode += BASE;
                }
            }
            
            //double check the String
            if (hashCode == targetCode){
                //注意此时的substring,在Java中是不包含最后一个的。边界处理的问题,左闭右开。
                if (source.substring(i - m + 1, i + 1).equals(target)) {
                    return i - m + 1;
                }
            }
        }
        return -1;
    }
}
posted @ 2022-01-12 20:47  DarkerG  阅读(139)  评论(0编辑  收藏  举报