【问题标题】:Find occurrences of the adjacent sub strings in the text查找文本中相邻子字符串的出现
【发布时间】:2020-04-15 06:37:09
【问题描述】:

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

“启动”是指贷款人以附件 A 的形式向银行发出的书面通知。

“启动通知”是指贷款人以附件 A 和启动的形式向银行发出的书面通知。

“工作日”是指银行开放进行一般业务和激活通知的每一天(周六和周日除外)。

关键字数组的样子

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

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

激活 - 4

激活通知 - 2

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

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

【问题讨论】:

  • 您的关键字是否可以重叠但不是子集?例如{"Activation Notice", "Notice from Lender"}。如果是这样,你更喜欢哪一个?
  • @mcskinner 关键字也可以重叠,子集是描述问题的特例
  • 如果文本是"Activation Notice from Lender",带有关键字{"Activation Notice", "Notice from Lender"},你会期望什么输出?现有方法将返回所有匹配项,每个匹配项 1 个。但也许你更喜欢更长的比赛?还是第一场比赛,然后按长度打破平局?
  • @mcskinner。抱歉,我查看了几个文档,关键字没有重叠,只是我描述的子集。所以我需要针对子集的情况的解决方案

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


【解决方案1】:

我假设您根据链接的示例得到了结果。

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. 候选人在活动范围之后开始。由于唯一的重叠是子集,并且这与活动范围重叠,因此它是一个开始较晚但仍结束于或之前的子集。

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

【讨论】:

  • 谢谢!经过简短的审查,这似乎是一个可行的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-22
  • 2010-12-25
  • 2012-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多