Scala =选项范围(p=>p.priority val(之前、之后)=选项范围(!=可能匹配) (possibleMatch.possibleTransations**Set(transitions.keys.toList:*).toList foreach(t=> 转换(t.get.removeOption(option)) _选项=之前:::之后 可计算的可能跃迁 案例无=> } } } 类可能匹配(val选项:字符串, 此优先级:Int, 匹配:匹配, 剩余:列表[字符串]){ 私有变量_优先级=此优先级 私有var匹配选项=列表(新匹配选项(优先级、匹配、剩余)) private var_possibleTransations=matchOptions映射(u.possibleTransations)reduceLeft(u++) 私有def ComputePossibleTransformations={ _PossibleTransations=matchOptions映射(0.PossibleTransations)reduceLeft(0++) } def优先级:Int=_优先级 def hasMatchOption(matched:matched):布尔值=matchOptions存在(u.matched==matched) def addMatchOption(优先级:Int,已匹配:已匹配,剩余:列表[字符串]):单位={ 如果(优先级(“”,w)) def possibleTransfions:Set[Char]=\u possibleTransfions def计算(s:状态,c:字符):单位={ def computeOptions(状态:状态, c:Char, 优先权:Int, 匹配:匹配, 剩余:列表[字符串]:单位={ 剩余比赛{ 案例w::ws=> 如果(!w.isEmpty&&w(0).toLowerCase==c.toLowerCase){ val newMatched=(w(0)+匹配的.head.\u 1,匹配的.head.\u 2.子字符串(1))::matched.tail val newPriority=if(匹配的.head.\u 1为空)(优先级-1)else优先级 如果(w.drop(1)为空) s、 addOption(option,newPriority-1,(“”,ws.head)::newMatched,ws) 其他的 s、 addOption(option,newPriority,newMatched,w.substring(1)::ws) } 如果(ws!=Nil)计算选项(s,c,优先级,(“”,ws.head)::匹配,ws) 案例无=> } } if(可能的转换包含c) matchOptions foreach(mO=>computeOptions(s、c、mO.priority、mO.matched、mO.remaining)) } } 类匹配选项(val优先级:Int, 匹配:匹配, 剩余val:列表[字符串]){ lazy val PossibleTransations:Set[Char]=Set(剩余映射((0)到小写):*) }

Scala =选项范围(p=>p.priority val(之前、之后)=选项范围(!=可能匹配) (possibleMatch.possibleTransations**Set(transitions.keys.toList:*).toList foreach(t=> 转换(t.get.removeOption(option)) _选项=之前:::之后 可计算的可能跃迁 案例无=> } } } 类可能匹配(val选项:字符串, 此优先级:Int, 匹配:匹配, 剩余:列表[字符串]){ 私有变量_优先级=此优先级 私有var匹配选项=列表(新匹配选项(优先级、匹配、剩余)) private var_possibleTransations=matchOptions映射(u.possibleTransations)reduceLeft(u++) 私有def ComputePossibleTransformations={ _PossibleTransations=matchOptions映射(0.PossibleTransations)reduceLeft(0++) } def优先级:Int=_优先级 def hasMatchOption(matched:matched):布尔值=matchOptions存在(u.matched==matched) def addMatchOption(优先级:Int,已匹配:已匹配,剩余:列表[字符串]):单位={ 如果(优先级(“”,w)) def possibleTransfions:Set[Char]=\u possibleTransfions def计算(s:状态,c:字符):单位={ def computeOptions(状态:状态, c:Char, 优先权:Int, 匹配:匹配, 剩余:列表[字符串]:单位={ 剩余比赛{ 案例w::ws=> 如果(!w.isEmpty&&w(0).toLowerCase==c.toLowerCase){ val newMatched=(w(0)+匹配的.head.\u 1,匹配的.head.\u 2.子字符串(1))::matched.tail val newPriority=if(匹配的.head.\u 1为空)(优先级-1)else优先级 如果(w.drop(1)为空) s、 addOption(option,newPriority-1,(“”,ws.head)::newMatched,ws) 其他的 s、 addOption(option,newPriority,newMatched,w.substring(1)::ws) } 如果(ws!=Nil)计算选项(s,c,优先级,(“”,ws.head)::匹配,ws) 案例无=> } } if(可能的转换包含c) matchOptions foreach(mO=>computeOptions(s、c、mO.priority、mO.matched、mO.remaining)) } } 类匹配选项(val优先级:Int, 匹配:匹配, 剩余val:列表[字符串]){ lazy val PossibleTransations:Set[Char]=Set(剩余映射((0)到小写):*) },scala,string-matching,trie,Scala,String Matching,Trie,不过,它确实需要一些重构。当我开始在博客上解释它时,我总是这样做。好吧,我刚刚意识到你想要的正是我的一个朋友对另一个问题的建议。所以,这是他的答案,根据你的需要简化了 class PrefixMatcher { // import scala.collection.Set // Scala 2.7 needs this -- and returns a gimped Set private var set = new scala.collection.immutable.TreeSet[

不过,它确实需要一些重构。当我开始在博客上解释它时,我总是这样做。

好吧,我刚刚意识到你想要的正是我的一个朋友对另一个问题的建议。所以,这是他的答案,根据你的需要简化了

class PrefixMatcher {
  // import scala.collection.Set // Scala 2.7 needs this -- and returns a gimped Set
  private var set = new scala.collection.immutable.TreeSet[String]()
  private def succ(s : String) = s.take(s.length - 1) + ((s.charAt(s.length - 1) + 1)).toChar

  def add(s: String) = set += s

  def findMatches(prefix: String): Set[String] = 
    if (prefix.isEmpty) set else set.range(prefix, succ(prefix))
}

使用Trie。尽管有些人发布了被错误命名为tries的排序树映射数据结构,但实际上还没有人在这里发布Trie。是Java中Trie实现的一个相当有代表性的示例。我不知道斯卡拉有什么。另请参阅关于尝试的解释。

@pyo:看起来您的for循环遍历了集合中的所有字符串,对吗?在java代码中,我调用了tailSet,它的作用类似于:向以按顺序指定的前缀开头的节点返回一个迭代器,这样我们找到匹配项的速度比比较集合中的每个项快得多。@Alex Black:的确,我在序列理解中错过了对.from的调用,现在编辑。@pyo:还有一个问题,当s.startsWith(prefix)像我发布的一样返回false时,您的实现是否会中断?不,它将在从前缀到集合末尾的每个字符串上测试startsWith。老实说,我不知道这个术语的意义,我只是从原始问题中保留了它。我现在将适时地编辑它;)有趣的东西,我现在不需要匹配前缀的组合,我会记住的。谢谢!我不允许别人这么容易看到这么难看的代码感谢您的建议,我认为对于这个场景,from语法看起来比子集更简单。在集合上迭代更简单。:-)不同之处在于运行时行为。为前缀选择子集是非常有选择性的,它将跳过树的大部分部分。我将使用“from”方法得到一个没有结尾的子集(因此您必须检查是否已到达结尾)与您建议的使用子集进行比较,我认为两者都跳过了树的大部分。是的,您是对的。我添加了一个from版本,当到达前缀末尾时(使用takeWhile)该版本会中止。我想这与您的实现和子集实现具有相同的运行时行为。很可读,很好。我试图给你投票,结果我已经给了你投票,所以它给了你投票,现在它说太老了,不能改变,对不起!!虽然尝试是树结构的,但我不认为树集将如何帮助您实现trie。在树集合中使用树只是为了利用元素的顺序来加速测试和插入。树方面不是它的API的一部分
import scala.collection.jcl.TreeSet;

class PrefixMatcher 
{
    private val _set = new TreeSet[String]

    def add(s: String) = _set.add(s)

    def findMatches(prefix: String): Iterable[String] =
        for (s <- _set.from(prefix) if s.startsWith(prefix)) yield s
}

object Main
{
    def main(args: Array[String]): Unit =
    {
        val pm = new PrefixMatcher()

        pm.add("fooBar")
        pm.add("fooCow")
        pm.add("barFoo")

        pm.findMatches("foo").foreach(println)
    }
}
class PrefixMatcher {
    private val _set = new TreeSet[String]
    def add(s: String) = _set.add(s)
    def findMatches(prefix: String): Iterable[String] =
        _set from prefix takeWhile(_ startsWith prefix)
}
class PrefixMatcher {
  private val _set = new java.util.TreeSet[String]

  def add(s: String) = _set.add(s)

  def findMatches(prefix: String) : Set[String] = {
    def inc(x : String) = { //ignores overflow
       assert(x.length > 0) 
       val last = x.length - 1
       (x take last) + (x(last) + 1).asInstanceOf[Char]
    }
   _set.subSet(prefix, inc(prefix))
  }
}
package com.blogspot.dcsobral.matcher.DFA

object DFA {
  type Matched = List[(String, String)]
  def words(s : String) = s.split("\\W").filter(! _.isEmpty).toList
}

import DFA._
import scala.runtime.RichString

class DFA {
  private val initialState : State = new State(None, "")
  private var currState : State = initialState
  private var _input : RichString = ""
  private var _badInput : RichString = ""
  private var _accepted : Boolean = true

  def accepted : Boolean = _accepted
  def input : String = _input.reverse + _badInput.reverse 

  def transition(c : Char) : List[(String, Matched)] = {
    if (c == '\b') backtrack
    else {
      if (accepted) {
        val newState = currState(c)
        newState match {
          case Some(s) => _input = c + _input; currState = s
          case None => _badInput = c + _badInput; _accepted = false
        }
      } else {
        _badInput = c + _badInput
      }
      optionList
    }
  }

  def transition(s : String) : List[(String, Matched)] = {
    s foreach (c => transition(c))
    optionList
  }

  def apply(c : Char) : List[(String, Matched)] = transition(c)
  def apply(s : String) : List[(String,Matched)] = transition(s)

  def backtrack : List[(String, Matched)] = {
    if(_badInput isEmpty) {
      _input = _input drop 1
      currState.backtrack match {
        case Some(s) => currState = s
        case None =>
      }
    } else {
      _badInput = _badInput drop 1
      if (_badInput isEmpty) _accepted = true
    }
    optionList
  }

  def optionList : List[(String, Matched)] = if (accepted) currState.optionList else Nil

  def possibleTransitions : Set[Char] = if (accepted) (currState possibleTransitions) else Set.empty

  def reset : Unit = {
    currState = initialState
    _input = ""
    _badInput = ""
    _accepted = true
  }

  def addOption(s : String) : Unit = {
    initialState addOption s
    val saveInput = input
    reset
    transition(saveInput)
  }
  def removeOption(s : String) : Unit = {
    initialState removeOption s
    val saveInput = input
    reset
    transition(saveInput)
  }
}

class State (val backtrack : Option[State],
             val input : String) {
  private var _options : List[PossibleMatch] = Nil
  private val transitions : scala.collection.mutable.Map[Char, State] = scala.collection.mutable.Map.empty
  private var _possibleTransitions : Set[Char] = Set.empty

  private def computePossibleTransitions = {
    if (! options.isEmpty)
      _possibleTransitions = options map (_.possibleTransitions) reduceLeft (_++_)
    else
      _possibleTransitions = Set.empty
  }

  private def computeTransition(c : Char) : State = {
    val newState = new State(Some(this), input + c)
    options foreach (o => if (o.possibleTransitions contains c) (o computeTransition (newState, c)))
    newState
  }

  def options : List[PossibleMatch] = _options
  def optionList : List[(String, Matched)] = options map (pm => (pm.option, pm.bestMatch))
  def possibleTransitions : Set[Char] = _possibleTransitions

  def transition(c : Char) : Option[State] = {
    val t = c.toLowerCase
    if (possibleTransitions contains t) Some(transitions getOrElseUpdate (t, computeTransition(t))) else None
  }

  def apply(c : Char) : Option[State] = transition(c)

  def addOption(option : String) : Unit = {
    val w = words(option)
    addOption(option, w.size, List(("", w.head)), w)
  }

  def addOption(option : String, priority : Int, matched : Matched, remaining : List[String]) : Unit = {
    options find (_.option == option) match {
      case Some(pM) => 
        if (!pM.hasMatchOption(matched)) {
          pM.addMatchOption(priority, matched, remaining)
          if (priority < pM.priority) {
            val (before, _ :: after) = options span (_ != pM)
            val (highPriority, lowPriority) = before span (p => p.priority < priority || 
              (p.priority == priority && p.option < option))
            _options = highPriority ::: (pM :: lowPriority) ::: after
          }
          transitions foreach (t => pM computeTransition (t._2, t._1))
        }
      case None => 
        val (highPriority, lowPriority) = options span (p => p.priority < priority || 
              (p.priority == priority && p.option < option))
        val newPM = new PossibleMatch(option, priority, matched, remaining)
        _options = highPriority ::: (newPM :: lowPriority)
        transitions foreach (t => newPM computeTransition (t._2, t._1))
    }
    computePossibleTransitions
  }

  def removeOption(option : String) : Unit = {
    options find (_.option == option) match {
      case Some(possibleMatch) =>
        val (before, _ :: after) = options span (_ != possibleMatch)
        (possibleMatch.possibleTransitions ** Set(transitions.keys.toList : _*)).toList foreach (t => 
          transition(t).get.removeOption(option))
        _options = before ::: after
        computePossibleTransitions
      case None =>
    }
  }
}

class PossibleMatch (val option : String, 
                     thisPriority : Int, 
                     matched : Matched, 
                     remaining : List[String]) {
  private var _priority = thisPriority
  private var matchOptions = List(new MatchOption(priority, matched, remaining))
  private var _possibleTransitions = matchOptions map (_.possibleTransitions) reduceLeft (_++_)
  private def computePossibleTransitions = {
    _possibleTransitions = matchOptions map (_.possibleTransitions) reduceLeft (_++_)
  }

  def priority : Int = _priority
  def hasMatchOption(matched : Matched) : Boolean = matchOptions exists (_.matched == matched)
  def addMatchOption(priority : Int, matched : Matched, remaining : List[String]) : Unit = {
    if (priority < _priority) _priority = priority
    val (highPriority, lowPriority) = matchOptions span (_.priority < priority)
    val newMO = new MatchOption(priority, matched, remaining)
    matchOptions = highPriority ::: (newMO :: lowPriority)
    computePossibleTransitions
  }
  def bestMatch : Matched = matchOptions.head.matched.reverse.map(p => (p._1.reverse.toString, p._2)) ::: 
    remaining.tail.map(w => ("", w))
  def possibleTransitions : Set[Char] = _possibleTransitions

  def computeTransition(s: State, c : Char) : Unit = {
    def computeOptions(state : State,
                       c : Char, 
                       priority : Int, 
                       matched : Matched, 
                       remaining : List[String]) : Unit = {
      remaining match {
        case w :: ws => 
          if (!w.isEmpty && w(0).toLowerCase == c.toLowerCase) {
            val newMatched = (w(0) + matched.head._1, matched.head._2.substring(1)) :: matched.tail
            val newPriority = if (matched.head._1 isEmpty) (priority - 1) else priority

            if (w.drop(1) isEmpty)
              s.addOption(option, newPriority - 1, ("", ws.head) :: newMatched , ws)
            else
              s.addOption(option, newPriority, newMatched, w.substring(1) :: ws)
          }
          if (ws != Nil) computeOptions(s, c, priority, ("", ws.head) :: matched, ws)
        case Nil =>
      }
    }

    if(possibleTransitions contains c)
      matchOptions foreach (mO => computeOptions(s, c, mO.priority, mO.matched, mO.remaining))
  }
}

class MatchOption (val priority : Int,
                   val matched : Matched,
                   val remaining : List[String]) {
  lazy val possibleTransitions : Set[Char] = Set( remaining map (_(0) toLowerCase) : _* )
}
class PrefixMatcher {
  // import scala.collection.Set // Scala 2.7 needs this -- and returns a gimped Set
  private var set = new scala.collection.immutable.TreeSet[String]()
  private def succ(s : String) = s.take(s.length - 1) + ((s.charAt(s.length - 1) + 1)).toChar

  def add(s: String) = set += s

  def findMatches(prefix: String): Set[String] = 
    if (prefix.isEmpty) set else set.range(prefix, succ(prefix))
}