用模式查找的最快方法

时间:2016-12-18 12:53:03

标签: c# data-structures

想象一下,我有一个包含数百个唯一名称的列表,例如

["john", "maria", "joseph", "richard", "samantha", "isaac", ...]

我可以通过匹配模式来存储这些以提供快速查找时间的最佳方式是什么?

我只需要匹配“面具”,想不出更好的词。

基本上,我收到了字母及其位置____a__(其中_代表一个未知字母。)然后我需要找到数据结构中与该掩码匹配的所有值,例如在这种情况下,它将返回“richard”,但也应该可以获得多个“返回”值。

2 个答案:

答案 0 :(得分:3)

似乎对“数百”名字的工作很多。在数百个名称的列表上进行线性搜索将非常快。现在,如果你说几十万或几百万......

在任何情况下,您都可以使用字典加快速度。您可以将数据预处理到字典中,字符的键是字符和位置的组合,值是在该位置包含该字符的单词。例如,如果您要索引“john”和“joseph”,那么您将拥有:

{'j',0},{"john","jospeh"}
{'o',1},{"john","joseph"}
{'h',2},{"john"}
{'n',3},{"john}
{'s',2},{"joseph"}
{'e',3},{"joseph"}
{'p',4},{"joseph"}
{'h',5},{"joseph"}

现在让我们说你给了面具“jo ....”(点是“不在乎”)。你可以把它变成两个键:

{'j',0}
{'o',1}

您在字典中查询索引为“j”的单词列表。然后查询字典以查找索引为“0”的单词列表。然后,您将与列表相交以获得结果。

这是一个简单的倒排索引,但是在字符而不是单词上。

列表本身将花费您总共O(m * n)个空格,其中m是单词的总数,n是字符的平均单词长度。最多,字典条目的数量将是26 * max_word_length。在实践中,它可能会少得多。

如果您将值设为HashSet<string>而不是List<string>,则交叉点会更快。但是,它会增加你的内存占用。

如果你的面具只包含几个字符,那应该比线性搜索更快。掩模越长,您必须交叉的列表就越多。

对于字典键,我建议:

public struct Key
{
    public char KeyChar;
    public int Pos;
    public override int GetHashCode()
    {
        return (int)KeyChar + Pos << 16;
    }
    public override bool Equals(object obj)
    {
        if (!obj is Key) return false;
        var other = (Key)obj;
        return KeyChar == other.KeyChar && Pos == other.Pos;
    }
}

所以你的字典将是Dictionary<Key, HashSet<string>>

答案 1 :(得分:1)

如果最长的单词有m个字母,那么你可以保留m个列表l [1],...,l [m],使得每个列表l [i]中的单词按字典顺序从第i个开始排序每个单词中的字母(较短的单词不会出现在该单词中)。然后,如果您的查询是$array2 = array( 'Here' => 'a', 'is' => 'y', 'my' => 'c', 'favorite' => 'u', 'team' => 'y', '.' => 'o' ); $search = 'y'; // Search parameter $freq = 0; // Frequency of 'y' in second array $result = array(); foreach ($array2 as $k => $arr) { if ($arr == 'y') { $freq++; // Increment every time y appears } /* If only one y appears, store the array keys in $result */ if ($freq == 1) { $result[] = $k; } /* If another y appears, break out from the loop such that $result stores all $array2 keys until the second y appears */ if ($freq == 2) { break; } } ,只需在列表l [4]中执行二进制搜索。

这将花费你内存中的O(mn)并花费O(m n log n)时间来构建,但是会给你O(log n)查询时间,这是你可以获得的最快。

修改
好消息,我最近偶然发现了range trees,这将允许你在某种程度上有效地执行这种查询,即在O(log ^ m(n)+ k)时间内,并且需要O(n log ^( d-1)(n))空间。

它们不是直接实现的,因为您需要构建一个二进制搜索树,用第一个字母对单词进行排序,然后为每个内部节点构建一个二叉搜索树,该节点存储该节点子树中的单词按第二个字母排序,依此类推。

另一方面,这将允许您执行更广泛的查询,即您可以查找连续的字母间隔,即Array ( [0] => is [1] => my [2] => favorite )

之类的模式