使用 C# 在 LeetCode 28“implement strStr()”中实现 Rabin-Karp 算法来搜索字符串的问题

Posted

技术标签:

【中文标题】使用 C# 在 LeetCode 28“implement strStr()”中实现 Rabin-Karp 算法来搜索字符串的问题【英文标题】:Issue with implementing Rabin-Karp algorithm to search string in LeetCode 28 "implement strStr()" using C# 【发布时间】:2015-09-15 02:16:34 【问题描述】:

我实现了这个算法来解决 Leetcode 的 #28 问题“implement strStr()”。 问题描述:实现strStr(), 返回 haystack 中第一次出现 needle 的索引,如果 needle 不是 haystack 的一部分,则返回 -1。

我的代码是根据http://www.geeksforgeeks.org/searching-for-patterns-set-3-rabin-karp-algorithm/ 的教程实现的。

我发现使用不同的素数来缩小哈希值,函数可能会出错。 这是我的代码:

    public class Solution 
    public int StrStr(string haystack, string needle) 
        int len = needle.Length;
        //2 special case
        if (haystack.Length < len) return -1;
        if (needle == "") return 0;

        //base prime number used for rabin-karp's hash function
        int basement = 101;
        //prime number used to scale down the hash value
        int prime = 101;
        //the factor used to multiply with the character to be removed from the hash
        int factor = (int)(Math.Pow(basement, needle.Length - 1)) % prime;

        //get ascii value of the needle and the initial window
        int needleHash = 0;
        int windowHash = 0;
        byte[] needleBytes = Encoding.ASCII.GetBytes(needle);
        byte[] windowBytes = Encoding.ASCII.GetBytes(haystack.Substring(0, len));

        //generate hash value for both
        for (int i = 0; i < needle.Length; i++)
        
            needleHash = (needleHash * basement + needleBytes[i]) % prime;
            windowHash = (windowHash * basement + windowBytes[i]) % prime;
        

        //loop to find match
        bool findMatch = true;
        for (int i = 0; i < haystack.Length - len + 1; i++)
            //if hash value matches, incase the hash value are not uniq, iterate through needle and window
            if(needleHash == windowHash)
                findMatch = true;
                for (int j = 0; j < len; j++)
                
                    if (needle[j] != haystack[i + j])
                    
                        findMatch = false;
                        break;
                    
                
                if (findMatch == true) return i;
            
            //move the sliding window and find the hash value for new window
            if (i < haystack.Length - len)
                byte removeByte = Encoding.ASCII.GetBytes(haystack.Substring(i, 1))[0];
                byte addByte = Encoding.ASCII.GetBytes(haystack.Substring(i + len, 1))[0];
                //function of rolling hash
                windowHash = ((windowHash - removeByte * factor) * basement + addByte) % prime;
                //ensure the window hash to be positive
                if(windowHash < 0) windowHash += prime;
            
        
        return -1;
    
  

将“prime”设置为“101”,此代码通过所有测试。但是,如果我将其更改为其他质数,无论更小还是更大(例如:17、31、103),它总是在测试“68/72”时失败

haystack = "baabbaaaaaaabbaaaaabbabbabaabbabbabbabbab 巴布巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴巴 bbbaabaabbabbaaaabababaaabbabbababbabbaaabbbbbbabbabbaabbbaa"; 针 = "bbaaaababa";

因此,我相信我的代码存在我无法检测到的大问题。为什么会这样?

【问题讨论】:

【参考方案1】:

您的代码有两个问题:

    basement 应根据您用作参考的站点的算法设置为 256。该值是输入字母表中的字符数。

    您对factor 的计算不正确;在计算余数之前,您将转换为 intMath.Pow 操作导致double 值大于Int32.MaxValue。当您将模运算转换为 int before 时,您会截断此值。您需要使用 double 值执行模运算并将 then 转换为 int。该行应如下所示:

    int 因子 = (int)((Math.Pow(basement, needle.Length - 1)) % prime);

我使用这些修改和给出的示例测试了您的代码,它适用于 17、31、101 和 103 的素数。

【讨论】:

感谢您的评论。以您的方式修改代码确实通过了所有测试:D。但是我想进一步讨论一下,对于第 1 期,请参阅wikipedia,basement 是一个大素数。但在我提到的网站中,它使用了256,这是 ascii 大小。我对地下室的选择感到困惑。对于第 2 期,我看不出在模计算之前或之后进行转换的区别,你能解释一下区别吗?

以上是关于使用 C# 在 LeetCode 28“implement strStr()”中实现 Rabin-Karp 算法来搜索字符串的问题的主要内容,如果未能解决你的问题,请参考以下文章

前端与算法 leetcode 28.实现 strStr()

Leetcode 28 - 实现 strStr():问题

leetcode C#语言题解三

leetcode C#语言题解四

C#刷遍Leetcode面试题系列连载 - 入门与工具简介

LeetCode - 28. Implement strStr()