簡體   English   中英

如何使用模糊匹配在字符串中查找子字符串的位置

[英]How to find a position of a substring within a string with fuzzy match

我遇到了在 OCR 識別的文本中匹配字符串的問題,並考慮到可以任意容忍錯誤、丟失或額外的字符來找到它的位置。 結果應該是最佳匹配位置,可能(不一定)具有匹配子字符串的長度。

例如:

String: 9912, 1.What is your name?
Substring: 1. What is your name?
Tolerance: 1
Result: match on character 7

String: Where is our caat if any?
Substring: your cat
Tolerance: 2
Result: match on character 10

String: Tolerance is t0o h1gh.
Substring: Tolerance is too high;
Tolerance: 1
Result: no match

我曾嘗試調整 Levenstein 算法,但它對子字符串不能正常工作並且不返回位置。

Delphi 中的算法將是首選,但任何實現或偽邏輯都可以。

這是一個有效的遞歸實現,但可能不夠快。 最糟糕的情況是找不到匹配項,並且“What”中除最后一個字符之外的所有字符在 Where 中的每個索引處都匹配。 在這種情況下,算法將對 Where 中的每個字符進行 Length(What)-1 + Tolerance 比較,加上每個 Tolerance 的遞歸調用。 由於容差和 What 的長度都是常量,我會說算法是 O(n)。 它的性能將隨着“What”和“Where”的長度線性下降。

function BrouteFindFirst(What, Where:string; Tolerance:Integer; out AtIndex, OfLength:Integer):Boolean;
  var i:Integer;
      aLen:Integer;
      WhatLen, WhereLen:Integer;

    function BrouteCompare(wherePos, whatPos, Tolerance:Integer; out Len:Integer):Boolean;
    var aLen:Integer;
        aRecursiveLen:Integer;
    begin
      // Skip perfect match characters
      aLen := 0;
      while (whatPos <= WhatLen) and (wherePos <= WhereLen) and (What[whatPos] = Where[wherePos]) do
      begin
        Inc(aLen);
        Inc(wherePos);
        Inc(whatPos);
      end;
      // Did we find a match?
      if (whatPos > WhatLen) then
        begin
          Result := True;
          Len := aLen;
        end
      else if Tolerance = 0 then
        Result := False // No match and no more "wild cards"
      else
        begin
          // We'll make an recursive call to BrouteCompare, allowing for some tolerance in the string
          // matching algorithm.
          Dec(Tolerance); // use up one "wildcard"
          Inc(whatPos); // consider the current char matched
          if BrouteCompare(wherePos, whatPos, Tolerance, aRecursiveLen) then
            begin
              Len := aLen + aRecursiveLen;
              Result := True;
            end
          else if BrouteCompare(wherePos + 1, whatPos, Tolerance, aRecursiveLen) then
            begin
              Len := aLen + aRecursiveLen;
              Result := True;
            end
          else
            Result := False; // no luck!
        end;
    end;

  begin

    WhatLen := Length(What);
    WhereLen := Length(Where);

    for i:=1 to Length(Where) do
    begin
      if BrouteCompare(i, 1, Tolerance, aLen) then
      begin
        AtIndex := i;
        OfLength := aLen;
        Result := True;
        Exit;
      end;
    end;

    // No match found!
    Result := False;

  end;

我使用以下代碼來測試該功能:

procedure TForm18.Button1Click(Sender: TObject);
var AtIndex, OfLength:Integer;
begin
  if BrouteFindFirst(Edit2.Text, Edit1.Text, ComboBox1.ItemIndex, AtIndex, OfLength) then
    Label3.Caption := 'Found @' + IntToStr(AtIndex) + ', of length ' + IntToStr(OfLength)
  else
    Label3.Caption := 'Not found';
end;

案例:

String: Where is our caat if any?
Substring: your cat
Tolerance: 2
Result: match on character 10

它顯示了長度為 6 的字符 9 的匹配。對於其他兩個示例,它給出了預期的結果。

這是模糊匹配(近似搜索)的完整示例,您可以根據需要使用/更改算法! https://github.com/alidehban/FuzzyMatch

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM