查找文本中相邻子字符串的出现

时间:2020-04-15 06:37:09

标签: c# algorithm string-search aho-corasick

我有Word文档的文本和字符串数组。目的是查找文档文本中这些字符串的所有匹配项。我尝试使用Aho-Corasick算法的Aho-Corasick string matching in C#实现,但是默认实现不适合我。 文字的典型部分看起来像

激活”是指实质上以附件A的形式从贷方向银行发出的书面通知。

激活通知”是指从贷款人到银行的书面通知,形式为附件A和激活。

营业日”是指银行开放用于日常营业和激活通知的每一天(周六和周日除外)。

关键字数组看起来像

var keywords = new[] {"Activation", "Activation Notice"};

Aho-Corasick算法的默认实现返回以下次数的计数

激活-4

激活通知-2

对于“激活说明”,它是正确的结果。但对于“激活”,正确的计数也应为2 因为我不需要考虑相邻关键字“激活通知”中的出现。

有适合​​这种情况的算法吗?

1 个答案:

答案 0 :(得分:0)

我假设您是根据链接的示例获得结果的。

StringSearchResult[] results = searchAlg.FindAll(textToSearch);

对于那些results,如果您假设唯一的重叠是子集,则可以按索引排序并在一次通过中收集所需的结果。

public class SearchResultComparer : IComparer<StringSearchResult> { 
    public int StringSearchResult(StringSearchResult x, StringSearchResult y) 
    { 
        // Try ordering by the start index.
        int compare = x.Index.CompareTo(y.Index);
        if (compare == 0)
        {
            // In case of ties, reverse order by keyword length.
            compare = y.Keyword.Length.CompareTo(x.Keyword.Length);
        }
        return compare;
    } 
} 

// ...


IComparer searchResultComparer = new SearchResultComparer();
Array.Sort(results, searchResultComparer); 

int activeEndIndex = -1;
List<StringSearchResult> nonOverlappingResults = new List<StringSearchResult>();
foreach(StringSearchResult r in results)
{
    if (r.Index < activeEndIndex)
    {
        // This range starts before the active range ends.
        // Since it's an overlap, skip it.
        continue;
    }

    // Save this result, track when it ends.
    nonOverlappingResults.Add(r);
    activeEndIndex = r.Index + r.Keyword.Length;
}

由于索引排序,该循环保证仅保留非重叠范围。但是某些范围将被拒绝。发生这种情况只能有两个原因。

  1. 候选者的起始索引与有效范围相同。由于排序打破了这些联系,因此最长的走在最前面,因此候选人必须短于活动范围,并且可以跳过。
  2. 候选人在有效范围之后开始。由于唯一的重叠是子集,并且与活动范围重叠,因此这是一个子集,其开始时间较晚,但仍在或之前结束。

因此,唯一被拒绝的候选者将是子集,并且必须在有效范围之前结束。因此,有效范围仍然是唯一需要担心与之重叠的事情。

相关问题