【问题标题】:Finding a pattern in a set在集合中寻找模式
【发布时间】:2010-09-21 04:42:47
【问题描述】:

我可以使用哪些算法来确定一组字符串中的常见字符?

为了使示例简单,我只关心连续 2 个以上的字符,以及它是否出现在 2 个或更多示例中。例如:

  1. 0000abcde0000
  2. 0000abcd00000
  3. 000abc0000000
  4. 00abc000de000

我想知道:

00用于1,2,3,4
000用于1,2,3,4
0000 用于 1,2,3
00000 用于 2,3
ab用于1,2,3,4
abc 用于 1,2,3,4
abcd 用于 1,2
bc 用于 1,2,3,4
bcd 用于 1,2
cd 用于 1,2
de 用于 1,4

【问题讨论】:

  • 你事先不知道你感兴趣的模式,对吧?

标签: algorithm design-patterns data-mining


【解决方案1】:

您可以使用距离矩阵的分析。任何对角线移动(无成本变化)都是完全匹配的。

【讨论】:

  • 好吧...我不知道你刚才说什么,但听起来不错。 :)
【解决方案2】:

这很可能是一个 NP 难题。它看起来类似于multiple sequence alignment,即。基本上,您可以根据需要调整多维 Smith-Waterman(= 局部序列比对)。不过,可能有更有效的算法。

【讨论】:

  • 我不这么认为。我认为它可以在 O(N*M) 时间内解决(N = 总字符数,M = 考虑的最大长度)
【解决方案3】:

您是否知道需要提前搜索的“价值”?或者您是否需要代码来解析字符串,并像您发布的那样为您提供统计信息?

如果您提前知道要查找的内容,则使用 Boyer-Moore 算法是判断子字符串是否存在(甚至定位它们)的一种非常快速的方法。

【讨论】:

  • 在这种情况下,搜索多个模式时,Wu-Manber 将比 Boyer-Moore 等单一模式搜索效率
【解决方案4】:

构建一棵树,其中通过树的路径是字母序列。让每个节点都包含一个“集合”,将字符串引用添加到其中(或者只保留一个计数)。然后跟踪单词中的 N 个位置,其中 N 是您关心的最长序列(例如,在每个字符处开始一个新句柄,在每个步骤中向下遍历所有句柄,并在 N 步后中止每个句柄)

这对于小型、有限和密集的字母表效果更好(DNA 是我认为第一个使用它的地方)。

编辑:如果您事先知道您关心的模式,可以通过提前构建树然后只检查您是否在树上而不是而不是扩展它。

一个例子

输入

abc
abd
abde
acc
bde

a : 4
  b : 3
    c : 1
    d : 2
      e : 1
  c : 1
    c : 1
b : 4
  d : 3
    e : 2
  c : 1
c : 3
  c : 1
d : 3
  e : 2

【讨论】:

  • 我不确定我是否关注,但这听起来很有趣......这是否在网上某处进行了说明,您可以给我一个链接吗?
  • 在答案中添加了一个示例(抱歉,我从未真正在任何地方看到过这种方法)
  • 谢谢...这个例子有帮助,但它只从字符串的开头开始。在您的示例中,您在不同的分支中出现了 3 次 DE。这有什么用?
  • DE 确实出现在三个不同的地方,但是如果你遵循 root->d->e 路径,你找到的计数是 2,因为“de”子字符串有 2 个地方成立。您无需在树中查找包含“de”的其他位置,因为它们也都计入 root->d->e。
  • 附注澄清:“de”在输入中出现两次,在树中出现三次。
【解决方案5】:

我假设这不是家庭作业。 (如果是,你就是你自己的抄袭!;-)

下面是一个快速而肮脏的解决方案。时间复杂度为O(m**2 * n),其中m 是平均字符串长度,n 是字符串数组的大小。

Occurrence 的实例保留包含给定字符串的索引集。 commonOccurrences 例程扫描一个字符串数组,为每个非空字符串调用captureOccurrencescaptureOccurrences 例程将当前索引放入Occurrence 中,用于给定字符串的每个可能的子字符串。最后,commonOccurrences 通过仅选择具有至少两个索引的Occurrences 来形成结果集。

请注意,您的示例数据中的常见子字符串比您在问题中确定的要多得多。例如,"00ab" 出现在每个输入字符串中。根据内容(例如所有数字、所有字母等)选择有趣字符串的附加过滤器——正如他们所说——留给读者作为练习。 ;-)

快速而肮脏的 JAVA 源:

package com.stackoverflow.answers;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class CommonSubstringFinder {

    public static final int MINIMUM_SUBSTRING_LENGTH = 2;

    public static class Occurrence implements Comparable<Occurrence> {
        private final String value;
        private final Set<Integer> indices;
        public Occurrence(String value) {
            this.value = value == null ? "" : value;
            indices = new TreeSet<Integer>();
        }
        public String getValue() {
            return value;
        }
        public Set<Integer> getIndices() {
            return Collections.unmodifiableSet(indices);
        }
        public void occur(int index) {
            indices.add(index);
        }
        public String toString() {
            StringBuilder result = new StringBuilder();
            result.append('"').append(value).append('"');
            String separator = ": ";
            for (Integer i : indices) {
                result.append(separator).append(i);
                separator = ",";
            }
            return result.toString();
        }
        public int compareTo(Occurrence that) {
            return this.value.compareTo(that.value);
        }
    }

    public static Set<Occurrence> commonOccurrences(String[] strings) {
        Map<String,Occurrence> work = new HashMap<String,Occurrence>();
        if (strings != null) {
            int index = 0;
            for (String string : strings) {
                if (string != null) {
                    captureOccurrences(index, work, string);
                }
                ++index;
            }
        }
        Set<Occurrence> result = new TreeSet<Occurrence>();
        for (Occurrence occurrence : work.values()) {
            if (occurrence.indices.size() > 1) {
                result.add(occurrence);
            }
        }
        return result;
    }

    private static void captureOccurrences(int index, Map<String,Occurrence> work, String string) {
        final int maxLength = string.length();
        for (int i = 0; i < maxLength; ++i) {
            for (int j = i + MINIMUM_SUBSTRING_LENGTH; j < maxLength; ++j) {
                String partial = string.substring(i, j);
                Occurrence current = work.get(partial);
                if (current == null) {
                    current = new Occurrence(partial);
                    work.put(partial, current);
                }
                current.occur(index);
            }
        }
    }

    private static final String[] TEST_DATA = {
        "0000abcde0000",
        "0000abcd00000",
        "000abc0000000",
        "00abc000de000",
    };
    public static void main(String[] args) {
        Set<Occurrence> found = commonOccurrences(TEST_DATA);
        for (Occurrence occurrence : found) {
            System.out.println(occurrence);
        }
    }

}

SAMPLE OUTPUT:(请注意,实际上每行只有一次出现;我似乎无法阻止块引用标记合并行)

“00”:0,1,2,3 “000”:0,1,2,3
“0000”:0,1,2 “0000a”:0,1
“0000ab”:0,1 "0000abc": 0,1
“0000abcd”:0,1 “000a”:0,1,2
“000ab”:0,1,2 "000abc": 0,1,2
“000abcd”:0,1 “00a”:0,1,2,3
“00ab”:0,1,2,3 "00abc": 0,1,2,3
“00abc0”:2,3 “00abc00”:2,3
“00abc000”:2,3 "00abcd": 0,1
“0a”:0,1,2,3 "0ab": 0,1,2,3
“0abc”:0,1,2,3 “0abc0”:2,3
“0abc00”:2,3 “0abc000”:2,3
“0abcd”:0,1 “ab”:0,1,2,3 “abc”:0,1,2,3 “abc0”:2,3 “abc00”:2,3
“abc000”:2,3 “abcd”:0,1 “公元前”:0,1,2,3 “bc0”:2,3 “bc00”:2,3
“bc000”:2,3 “bcd”:0,1 “c0”:2,3 “c00”:2,3 “c000”:2,3 “cd”:0,1
“德”:0,3 “de0”:0,3 “de00”:0,3
“e0”:0,3 “e00”:0,3

【讨论】:

    【解决方案6】:

    在网络上查找“后缀树”。或者选择 Dan Gusfield 的“字符串、树和序列的算法”。我没有要验证的书,但wikipedia page on suffix trees 说第 205 页包含解决您的问题的方法:“找到一组中至少 k 个字符串共有的最长子字符串”。

    【讨论】:

      【解决方案7】:

      您可能会发现suffix array 比后缀树更简单、更高效,具体取决于数据中常见子字符串的频率——如果它们足够常见,您将需要更复杂的后缀数组构造算法. (天真的方法是只使用您的库排序功能。)

      【讨论】:

        猜你喜欢
        • 2019-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-11
        • 2017-08-17
        • 2022-06-16
        • 2017-11-11
        相关资源
        最近更新 更多