字符串匹配的 Rabin-Karp 方法

问题描述

使用rabin-Karp方法进行字符串匹配,该方法使用了多少个字符-字符匹配? 哪里:

source  = abcabcabcacd
pattern = bcac

解决方法

让我们从算法本身开始。 Rabin-Karp 方法如何工作。我将使用 C# 代码来说明。 有

  string target = "bcac";
  string source = "abcabcabcacd";

我们计算 target 的哈希值:

  int targetHash = RabinHash(target);

然后有效地计算所有长度为 target 的子串的哈希值(这里是长度为 4 的子串): "abca""bcab"、... "cacd"。如果 targetHash equals 等于子串的散列,我们比较这个子串和 target 对应的字母。例如:

if targetHash = 888 并且对于子串我们有,比如说,

  abca : 555 
  bcab : 345 
  cabc : 888 <- check this (failure due to hash collision)
  abca : 555 
  bcab : 345 
  cabc : 888 <- check this (failure due to hash collision) 
  abca : 555 
  bcac : 888 <- check this (success)
  cacd : 900  
 

这里我们有 3 集字符-字符匹配。现在我们准备好回答您的问题:数字 字符匹配取决于哈希函数。

最坏的情况:我们只有哈希冲突:

  int RabinHash(string value) => 1;

所有 9 子串都应该进行字符-字符检查。

最好的情况:我们没有哈希冲突;比如说,我们可以实现一个典型的 Rabin 哈希函数

  int RabinHash(string value) => value.Aggregate(0,(s,a) => s * 26 + a - 'a');

我们应该检查唯一的1个实际匹配:

  string target = "bcac";
  string source = "abcabcabcacd";

  // int RabinHash(string value) => 1;
  int RabinHash(string value) => value.Aggregate(0,a) => s * 26 + a - 'a');

  int targetHash = RabinHash(target);

  var report = Enumerable
    .Range(0,source.Length - target.Length + 1)
    .Select(i => source.Substring(i,target.Length))
    .Select(chunk => new { 
      chunk,hash = RabinHash(chunk)
    })
    .Select(item => $"{item.chunk} : {item.hash,5} {(item.hash == targetHash ? "<- check it" : "")}");

  Console.Write(report);

结果:

  abca :   728 
  bcab : 18929 
  cabc : 35180 
  abca :   728 
  bcab : 18929 
  cabc : 35180 
  abca :   728 
  bcac : 18930 <- check it
  cacd : 35207

最终答案:根据哈希函数,我们必须检查 19 次。