首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我怎样才能加速我的Aho-Corasick算法?

我怎样才能加速我的Aho-Corasick算法?
EN

Stack Overflow用户
提问于 2018-05-29 04:07:45
回答 2查看 750关注 0票数 0

我正试图解决一个关于HackerRank的问题:“确定DNA健康”。在看了一些讨论后,我决定使用algorithm算法是最好的选择。这个问题涉及到搜索具有关联值的各种序列的字符串。任务是从给定列表中获取这些序列值对的一个分段,并找到与输入字符串相关联的值。这意味着使用100000个序列值对的列表执行44850次。我已经实现了该算法,虽然它比我的第一次尝试快得多,但它仍然不够快,无法通过这个测试用例。以下是我的实现:

建造trie:

代码语言:javascript
复制
def createValueTrie(gs: Array[(String, Int)]): TrieNodeWithVal = {
def recurse(genes: Array[(String, Int)]): Map[Char, TrieNodeWithVal] = {
  genes
    .groupBy(_._1.head)
    .map(x => (x._1, x._2.map(y => (y._1.tail, y._2))))
    .map{
      case (c, arr: Array[(String, Int)]) => {
        val value = arr.filter(_._1.length == 0).foldLeft(0)(_ + _._2)
        val filtered = arr.filter(_._1.length > 0)
        val recursed = recurse(filtered)
        (c, new TrieNodeWithVal(arr.exists(_._1.length == 0), recursed, value))
      }
    }
  }
  new TrieNodeWithVal(false, recurse(gs), 0)
}

在trie中搜索:

代码语言:javascript
复制
def findValueMatches(trie: TrieNodeWithVal, sequence: String): Iterator[(String, Long)] = {
    sequence.scanRight("")(_ + _).dropRight(1).iterator.flatMap(s => {
      Iterator.iterate[(Iterator[Char], Option[TrieNodeWithVal])]((s.iterator, Some(trie))) {
        case (it: Iterator[Char], Some(node)) => if (it.hasNext) (it, node(it.next())) else (it, None)
        case (it: Iterator[Char], None) => (it, None)
      }.takeWhile {
        case (_, Some(_)) => true
        case _ => false
      }.map {
        case (_, Some(node)) => node
      }.zipWithIndex.withFilter {
        case (node, _) => node isWord
      }.map {
        case (node, i) => (s.slice(0, i), node.value)
      }
    })
  }

Trie节点类:

代码语言:javascript
复制
class TrieNode(isAWord: Boolean, childs: Map[Char, TrieNode]) {
    val isWord = isAWord
    val children: Map[Char, TrieNode] = childs

    def apply(c: Char): Option[TrieNode] = children.get(c)

    override def toString(): String = "(" + children.map(x => (if (x._2.isWord) x._1.toUpper else x._1) + ": " + x._2.toString()).mkString(", ") + ")"
  }

  class TrieNodeWithVal(isAWord: Boolean, childs: Map[Char, TrieNodeWithVal], valu: Long) extends TrieNode(isAWord, childs) {
    val value = valu
    override val children: Map[Char, TrieNodeWithVal] = childs

    override def toString(): String = "(" + children.map(x => (if (x._2.isWord) x._1.toUpper + "[" + x._2.value + "]" else x._1) + ": " + x._2.toString()).mkString(", ") + ")"

    override def apply(c: Char): Option[TrieNodeWithVal] = children.get(c)
  }

我知道,对于失败案例,这里可以进行更多的边缘构建,但是讨论中的几个人说,由于每个查询都需要重新构建trie,所以这样做会慢一些。对于这样的问题,我应该使用更有效的集合吗?我如何在维护纯功能风格的同时加快速度呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-05-29 10:33:32

有各种各样的变化,一些可能会影响性能,而另一些只是化妆品。

recurse中,可以组合两个map调用,并使用partition来减少测试数组的次数:

代码语言:javascript
复制
def recurse(genes: Array[(String, Int)]): Map[Char, TrieNodeWithVal] = {
  genes
    .groupBy(_._1.head)
    .map { x =>
      val c = x._1
      val arr = x._2.map(y => (y._1.tail, y._2))

      val (filtered, nonFiltered) = arr.partition(_._1.nonEmpty)
      val value = nonFiltered.foldLeft(0)(_ + _._2)
      val recursed = recurse(filtered)
      (c, new TrieNodeWithVal(nonFiltered.nonEmpty, recursed, value))
    }
}

您可以通过对findValueMatches语句使用条件并结合一些操作来简化case

代码语言:javascript
复制
def findValueMatches(trie: TrieNodeWithVal, sequence: String): Iterator[(String, Long)] = {
  sequence.scanRight("")(_ + _).dropRight(1).iterator.flatMap(s => {
    Iterator.iterate[(Iterator[Char], Option[TrieNodeWithVal])]((s.iterator, Some(trie))) {
      case (it: Iterator[Char], Some(node)) if it.hasNext => (it, node(it.next()))
      case (it: Iterator[Char], _) => (it, None)
    }.takeWhile {
      _._2.nonEmpty
    }.zipWithIndex.collect {
      case ((_, Some(node)), i) if node.isWord =>
       (s.slice(0, i), node.value)
    }
  })
}

最后,可以使用val参数简化类。

代码语言:javascript
复制
class TrieNode(val isWord: Boolean, val children: Map[Char, TrieNode]) {
  def apply(c: Char): Option[TrieNode] = children.get(c)

  override def toString(): String = "(" + children.map(x => (if (x._2.isWord) x._1.toUpper else x._1) + ": " + x._2.toString()).mkString(", ") + ")"
}

class TrieNodeWithVal(isAWord: Boolean, childs: Map[Char, TrieNodeWithVal], val value: Long) extends TrieNode(isAWord, childs) {
  override val children: Map[Char, TrieNodeWithVal] = childs

  override def toString(): String = "(" + children.map(x => (if (x._2.isWord) x._1.toUpper + "[" + x._2.value + "]" else x._1) + ": " + x._2.toString()).mkString(", ") + ")"

  override def apply(c: Char): Option[TrieNodeWithVal] = children.get(c)
}

这都是编译过的,但没有经过测试,所以如果我无意中更改了算法,请原谅。

票数 1
EN

Stack Overflow用户

发布于 2018-09-28 16:12:15

你可以用三元组来尝试这个算法。My实现:https://github.com/Tetramatrix/phpahocorasick

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50576168

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档