C# 字符串解析与匹配算法

C# 字符串解析与匹配算法,c#,string,algorithm,version,string-matching,C#,String,Algorithm,Version,String Matching,我正在解决以下问题: 假设我有一个软件包列表,它们的名称可能如下所示(唯一已知的是这些名称的格式类似于SOMETHING+VERSION,这意味着版本总是在名称之后出现): 现在,我需要解析这个列表并只选择每个包的最新版本。对于本例,预期结果为: Efficient-Exclusive.Zip.Archiver(2011)-126.24-X Zip-Archiver.v15.08-T Custom.Zip.Archiver1.08 我目前使用的方法可以用以下方式描述: Split the i

我正在解决以下问题:

假设我有一个软件包列表,它们的名称可能如下所示(唯一已知的是这些名称的格式类似于
SOMETHING+VERSION
,这意味着版本总是在名称之后出现):

现在,我需要解析这个列表并只选择每个包的最新版本。对于本例,预期结果为:

Efficient-Exclusive.Zip.Archiver(2011)-126.24-X
Zip-Archiver.v15.08-T
Custom.Zip.Archiver1.08

我目前使用的方法可以用以下方式描述:

Split the initial strings into groups by their starting letter,
ignoring spaces, case and special symbols.
(`E`, `Z`, `C` for the example list above)

Foreach element {

    Apply the regular expression (or a set of regular expressions),
    which tries to deduce the version from the string and perform
    the following conversion `STRING -> (VERSION, STRING_BEFORE_VERSION)`

    // Example for this step:
    // 'Efficient.Exclusive.Zip.Archiver-PROPER.v.122.24-EXTENDED' ->
    // (122.24, Efficient.Exclusive.Zip.Archiver-PROPER)

    Search through the corresponding group (in this example - the 'E' group)
    and find every other strings, which starts from the 'STRING_BEFORE_VERSION' or
    from it's significant part. This comparison is performed in ignore-case and
    ignore-special-symbols mode.

    // The matches for this step:
    // Efficient.Exclusive.Zip.Archiver-PROPER, {122.24}
    // Efficient.Exclusive.Zip.Archiver, {123.01}
    // Efficient-Exclusive.Zip.Archiver, {126.24, 2011}

    // The last one will get picked, because year is ignored.

    Get the possible version from each match, ***pick the latest, yield that match.***

    Remove every possible match (including the initial element) from the list.
}
这个算法(我假设)应该适用于
O(N*V+N lg N*M)
,其中
M
代表平均字符串匹配时间,
V
代表版本regexp工作时间


然而,我怀疑有更好的解决方案(总是有!),可能是特定的数据结构或更好的匹配方法

如果你可以提出一些建议对当前方法做一些说明,请不要犹豫。这个怎么样?(伪代码)

Dictionary latestPackages=新字典(packageNameComparer);
foreach元素
{
(包,版本)=applyRegex(元素);
如果(!latestPackages.ContainsKey(包)| |较新)
{
最新包[包]=版本;
}
}
//打印最新的包装
字典操作是O(1),所以您有O(n)个总运行时。不需要预先分组,只存储当前最新的匹配项,而不是存储所有匹配项


字典有一个接受IEqualityComparer对象的构造函数。在那里,您可以实现自己的包名称之间相等的语义。但是请记住,在这个IEQualyExpor中需要实现一个GethHAc垢方法,它应该为您认为相等的对象返回相同的值。要重现上面的示例,可以为字符串中的第一个字符返回哈希代码,这将重现字典中的分组。但是,使用更智能的哈希代码可以获得更高的性能,因为它不会有太多冲突。如果仍然能产生好的效果,也许可以使用更多的字符。

我想你可以使用一个DAWG(http://en.wikipedia.org/wiki/Directed_acyclic_word_graph)这里有很好的效果。我认为您可以简单地循环每个节点,直到找到一个只有1个“子节点”的节点。在这个节点上,在树的“上方”有公共前缀,在下面有版本字符串。在此基础上,通过删除所有不是数字或句点的内容、按句点拆分字符串并将数组的每个元素转换为整数来解析版本字符串。这将为每个版本字符串提供一个int数组。识别最高版本,记录它,然后只带一个子节点移动到下一个节点


编辑:填充一个大的DAWG是一个非常昂贵的操作,但是查找速度非常快。

问题是我不能总是执行精确的(包、版本)拆分,通常我会得到一个(文本、包含名称、版本)对。因此,我应该能够匹配名字,例如匹配他们的前70%(或者类似的东西)。是否可以通过某种方式调整字典来进行这种比较(还包括忽略大小写和-特殊符号)?我怀疑使用字典很难做到这一点,因为字典是基于哈希的,但我可能错了。我也认为使用一些经过调整的后缀树实现可以做到这一点,但我怀疑由此产生的速度是否值得(由于实现的复杂性)。我添加了更多信息。如果您需要n个字符的通用前缀,这样就不会有很多包共享相同的前缀,那么您可以基于此前缀计算哈希代码(在自定义比较器中)。字典版本可能是最容易实现和理解的版本,如果操作正确,它的性能应该足以支持大量的n。如果n在几千个条目的范围内,即使是原始版本也不会有性能问题。也许找到最长的公共子字符串会有所帮助。你呢可以在SO和Web上的其他地方找到许多讨论和实现。@Jim最长的公共子字符串问题有助于两个字符串之间的比较,但不能优化要进行的比较的数量。我知道OP已经知道如何确定两个包是否相等,只想使用SO更快地进行比较你的运行时分析是错误的:它是O(N^2),而不是O(N log N)(常数因子已删除),因为你只基于第一个字符进行分组,该字符的范围有限(字母表),并且你将每个条目与同一组中的所有其他条目进行比较。
Split the initial strings into groups by their starting letter,
ignoring spaces, case and special symbols.
(`E`, `Z`, `C` for the example list above)

Foreach element {

    Apply the regular expression (or a set of regular expressions),
    which tries to deduce the version from the string and perform
    the following conversion `STRING -> (VERSION, STRING_BEFORE_VERSION)`

    // Example for this step:
    // 'Efficient.Exclusive.Zip.Archiver-PROPER.v.122.24-EXTENDED' ->
    // (122.24, Efficient.Exclusive.Zip.Archiver-PROPER)

    Search through the corresponding group (in this example - the 'E' group)
    and find every other strings, which starts from the 'STRING_BEFORE_VERSION' or
    from it's significant part. This comparison is performed in ignore-case and
    ignore-special-symbols mode.

    // The matches for this step:
    // Efficient.Exclusive.Zip.Archiver-PROPER, {122.24}
    // Efficient.Exclusive.Zip.Archiver, {123.01}
    // Efficient-Exclusive.Zip.Archiver, {126.24, 2011}

    // The last one will get picked, because year is ignored.

    Get the possible version from each match, ***pick the latest, yield that match.***

    Remove every possible match (including the initial element) from the list.
}
Dictionary<string,string> latestPackages=new Dictionary<string,string>(packageNameComparer);

foreach element
{
    (package,version)=applyRegex(element);

    if(!latestPackages.ContainsKey(package) || isNewer)
    {
        latestPackages[package]=version;
    }
}

//print out latestPackages