使用 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
的计算不正确;在计算余数之前,您将转换为 int
。 Math.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 算法来搜索字符串的问题的主要内容,如果未能解决你的问题,请参考以下文章