【问题标题】:Search a list of keywords [closed]搜索关键字列表[关闭]
【发布时间】:2013-09-24 23:05:42
【问题描述】:

我正在构建一个消息传递反垃圾邮件解决方案,我必须将收到的每条短信与一个关键字列表进行比较,如果该短信包含列表中的一个关键字,我必须删除它。

问题是搜索关键字列表的最佳算法是什么?示例如下

text message received is "hi how are you, visit us at www.xyz.com" 

列表示例如下

www.abc.com
www.xyz.com
...
...

【问题讨论】:

  • 谢谢克里斯,我做到了,请看一下结果,你会发现它没有那么有用。我正在寻找一种特定类型的搜索
  • 那么您要查找的具体搜索类型是什么?
  • 如果关键字集是常量,那么 Aho-Corasick 算法可能有用。

标签: string algorithm search


【解决方案1】:

如果有很多关键字,尤其是具有共同前缀的关键字,trie 在这里可能会很好用。

我假设您想要子字符串,而不仅仅是单词,即给定关键字 bah,它会在 bahama 中找到 bah。修改它以防止这种情况应该不难。

我还假设您没有关键字,并且它是关键字的子字符串(即 bahbahama 不能都是关键字)。满足这一点也应该不会太难。

只是,对于字符串中的每个字符,从树的顶部开始搜索并继续搜索树中的每个现有指针。一旦其中一个指针到达一个有效的单词,就随心所欲地使用它,并可能删除树中的所有指针。

复杂性:

O(max(n<sup>2</sup>, mn)) 其中m 是树中的节点数,在最坏的情况下,虽然平均情况下的性能应该会好很多。

示例:

所以,假设我们有关键字:

ab
b
caa

我们可能会得到一棵树:

      o
     /|\
  a / | \ c
   /  |b \
  o   o   o
  | b     | a
  o       o
          | a
          o

o 只是一个节点)

现在,对于输入字符串caab,我们先看c:(x表示树中的指针)

      o
     /|\
  a / | \ c
   /  |b \
  o   o   x
  | b     | a
  o       o
          | a
          o

注意右边的新指针。

然后a:

      o
     /|\
  a / | \ c
   /  |b \
  x   o   o
  | b     | a
  o       x
          | a
          o

注意左边的新指针和右边的前进指针。

然后a:

      o
     /|\
  a / | \ c
   /  |b \
  o   o   o
  | b     | a
  o       o
          | a
          x

注意左边的指针消失了,右边的指针前进了。

现在我们删除右边的那个,因为我们找到了一个有效的单词。

然后b:

      o
     /|\
  a / | \ c
   /  |b \
  o   x   o
  | b     | a
  o       o
          | a
          o

注意中间的新指针,然后我们也将其删除,因为我们找到了一个有效的单词。

【讨论】:

    【解决方案2】:

    您在谈论多少个关键字?研究 Boyer-Moore 字符串搜索算法,它可能适合您的目的,并且实现起来并不难。这是取自wikipedia article的java实现:

     /**
       * Returns the index within this string of the first occurrence of the
       * specified substring. If it is not a substring, return -1.
       *
       * @param haystack The string to be scanned
       * @param needle The target string to search
       * @return The start index of the substring
       */
      public static int indexOf(char[] haystack, char[] needle) {
        if (needle.length == 0) {
          return 0;
        }
        int charTable[] = makeCharTable(needle);
        int offsetTable[] = makeOffsetTable(needle);
        for (int i = needle.length - 1, j; i < haystack.length;) {
          for (j = needle.length - 1; needle[j] == haystack[i]; --i, --j) {
            if (j == 0) {
              return i;
            }
          }
          // i += needle.length - j; // For naive method
          i += Math.max(offsetTable[needle.length - 1 - j], charTable[haystack[i]]);
        }
        return -1;
      }
    
      /**
       * Makes the jump table based on the mismatched character information.
       */
      private static int[] makeCharTable(char[] needle) {
        final int ALPHABET_SIZE = 256;
        int[] table = new int[ALPHABET_SIZE];
        for (int i = 0; i < table.length; ++i) {
          table[i] = needle.length;
        }
        for (int i = 0; i < needle.length - 1; ++i) {
          table[needle[i]] = needle.length - 1 - i;
        }
        return table;
      }
    
      /**
       * Makes the jump table based on the scan offset which mismatch occurs.
       */
      private static int[] makeOffsetTable(char[] needle) {
        int[] table = new int[needle.length];
        int lastPrefixPosition = needle.length;
        for (int i = needle.length - 1; i >= 0; --i) {
          if (isPrefix(needle, i + 1)) {
            lastPrefixPosition = i + 1;
          }
          table[needle.length - 1 - i] = lastPrefixPosition - i + needle.length - 1;
        }
        for (int i = 0; i < needle.length - 1; ++i) {
          int slen = suffixLength(needle, i);
          table[slen] = needle.length - 1 - i + slen;
        }
        return table;
      }
    
      /**
       * Is needle[p:end] a prefix of needle?
       */
      private static boolean isPrefix(char[] needle, int p) {
        for (int i = p, j = 0; i < needle.length; ++i, ++j) {
          if (needle[i] != needle[j]) {
            return false;
          }
        }
        return true;
      }
    
      /**
       * Returns the maximum length of the substring ends at p and is a suffix.
       */
      private static int suffixLength(char[] needle, int p) {
        int len = 0;
        for (int i = p, j = needle.length - 1;
             i >= 0 && needle[i] == needle[j]; --i, --j) {
          len += 1;
        }
        return len;
      }
    

    【讨论】:

    • 虽然这对单次搜索很有效,但如果有很多关键字,它可能比其他方法效率低得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-08
    • 2019-07-26
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多