匹配包含“不关心”的二进制模式

时间:2014-02-10 09:47:04

标签: algorithm search data-structures

我有一组位模式,想要找到集合中与给定输入匹配的元素的索引。位模式包含“不关心”位,即x-es,它匹配0和1。

示例 位模式集是

index abcd
   0  00x1
   1  01xx
   2  100x
   3  1010
   4  1x11

然后,尝试匹配0110应返回索引1,而1011应返回索引4.

如何通过元素线性搜索更快地解决这个问题?我想可以制作一种二叉树,但是,创建这样一棵树的智能方法是什么?是否存在针对此类问题的其他有效数据结构/算法,主要是在查询效率和存储要求方面。

  • 位模式为64位(或更多)
  • 集合中元素的数量将为10 ^ 5 - 10 ^ 7
  • 并非所有位组合都在集合中表示,例如在示例中未表示0000
  • 数据集中将有大量的x-es
  • 位字符串只匹配集合
  • 中的一个元素

我有两种不同的情况需要解决这个问题

  • 案例1:我有可能进行大量的预计算
  • 案例2:新元素将动态添加到集合中

更新 x-es比其他位置更有可能出现在某些位位置,也就是说,某些位位置将由x-es支配,而其他位将主要为零/ 1。

5 个答案:

答案 0 :(得分:2)

我认为你可以为位模式构建一个trie树,该节点包含模式的原始索引。

完成匹配只是在trie树中搜索,当trie节点包含相同的'x'位时,转到下一个节点。结果可能包含特定输入的多个索引。

这是我的解决方案,

public class Solution {

    public static class Trie<T> {
        private final Character WILD = 'x';
        private Map<Character, Trie> children;
        private boolean isNode;
        private T value;

        public Trie() {
            children = new HashMap<Character, Trie>();
            isNode = false;
            value = null;
        }

        public void insert(String key, T value) {
            Trie<T> current = this;
            for (int i = 0; i < key.length(); i++) {
                char c = key.charAt(i);
                if (current.children.containsKey(c)) {
                    current = current.children.get(c);
                } else {
                    Trie<T> next = new Trie();
                    current.children.put(c, next);
                    current = next;
                }
            }
            current.isNode = true;
            current.value = value;
        }

        public List<T> get(String key) {
            List<T> result = new ArrayList<T>();
            get(this, key.toCharArray(), 0, result);
            return result;
        }

        private void get(Trie<T> trie, char[] chars, int index, List<T> result) {
            if (index == chars.length) {
                if (trie != null && trie.isNode) {
                    result.add(trie.value);
                }
                return;
            }
            char c = chars[index];
            if (trie.children.containsKey(c)) {
                get(trie.children.get(c), chars, index + 1, result);
            }
            if (trie.children.containsKey(WILD)) {
                get(trie.children.get(WILD), chars, index + 1, result);
            }
        }
    }

    public static void main(String[] args) {
        Trie<Integer> trie = new Trie<Integer>();
        trie.insert("00x1", 0);
        trie.insert("01xx", 1);
        trie.insert("100x", 2);
        trie.insert("1010", 3);
        trie.insert("1x11", 4);
        System.out.println(trie.get("0110")); // [1]
        System.out.println(trie.get("1011")); // [4]
    }
}

答案 1 :(得分:2)

您可以在此处构建一个与字符串长度成线性匹配的自动机。例如,你可以在(减少的,有序的)binary decision diagram中存储字符串集 - 或者实际上是字符串上的函数。我怀疑任何一组字符串的BDD都会在符号总数中具有线性大小,但我没有证据。

BDD解决方案与Qiang Jin的优秀解决方案类似,但略有不同,其中构造肯定需要线性空间,但在最坏的情况下查询对我来说并不明显(对我而言)。

答案 2 :(得分:1)

我认为,模式容器的解决方案将是一个特定的有序树。

  • 树的节点会说:
    • 它的位置(node.position)
    • 这个位置是什么位(0,1,x)(node.value)
  • 只有叶节点可以将x作为值。
  • 孩子的位置应始终大于父母的位置 - 排除重复的分支。
  • 如果某个节点有多个子节点,则会对它们进行排序:
    • 首先按位置
    • 两个具有相同位置的孩子,第一个是值为0的孩子。
  • 此类树的根节点为空。
  • 读取树,所以:
    • 从root开始,获取叶子的路径,取1和0并将它们放在适当的位置。
    • 当我们到达x时,用x-es填充所有空位。
    • 如果我们没有到达x,则叶子具有1/0值并且图案被填充。如果没有填写,则发生错误。

该节点中的匹配不应该由叶子完成,而是由级别完成。一个级别将是一个父母的子集。

Take first level of the children as current level
Take the first child on the level for current
  read currentnode.position
  check the appropriate position in the matched string against child value. 
  If it fits, go higher up the tree.
  If it doesn't fit, go to next child.
  If we are out of children on the level, go down the tree.

模式添加和二进制字符串匹配的复杂性在这里是log(n)。 如果有x%的x',则时间会缩短约为%,与@Qiang Jin的解决方案相反。搜索多分支树比仅仅三分支树更快。

我会将该树实现为列表层次结构。

答案 3 :(得分:0)

如果x模式的总数相对较小,你可以保留它们的列表,并使用哈希表来解决密钥中所有x都设置为1的元素(或者零,无关紧要)

然后查找查询及其所有修改后的表单,即根据x模式更改某些查询位的位置。 (如示例所示,也许可以检查哪些x模式会修改查询效率。)

举个例子:

index  abcd    hash-key  x-patterns
0      00x1 => 0011      0010
1      01xx => 0111      0011
2      100x => 1001      0001
3      1010 => 1010      N/A
4      1x11 => 1111      0100

To match 0110, the first x-pattern does not modify 0110; 0111 matches index 1.
To match 1011, the first 3 x-patterns do not modify 1011; 1111 matches index 4. 

JavaScript代码:

var hash = {3: 0, 7: 1, 9: 2, 10: 3, 15: 4}
  , x_patterns = [2,3,1,4]

function lookup(query){
    var mask = query ^ (Math.pow(2,31) - 1)

    if (hash[query]){
       return hash[query]
    } else {
       var i = 0
       while (x_patterns[i]){
          if (mask & x_patterns[i])
              if (hash[query | x_patterns[i]])
                  return hash[query | x_patterns[i]]
          i++
       }
       return false
    }
}

console.log(lookup(11), lookup(6))

输出:

4 1

答案 4 :(得分:0)

我可以考虑一个快速而简单的解决方案,

首先,它的“不关心”的数量很少,你可以简单地扩展索引并使用常规哈希(python dict,C ++ map等),在这种情况下:

index abcd
   0  00x1

成为这个:

index abcd
   0  0001
   1  0011

对索引的搜索是最快的。

希望它有所帮助!