我有一个带两个字符串的函数,s和obj。它通过删除1个字符来检查是否可以从字符串s形成字符串obj。实现工作正常但如果字符串变大则变得非常慢。我试图找到一种方法来使这段代码更快地工作。有人能想出一个实现吗?
def check_extra_char(s: String, obj: String): Boolean = {
if(s.length != obj.length+1) return false // Automatically false if obj is not one char smaller than s
for (i <- 0 until s.length)
if (s.take(i) + s.substring(1+i) == obj) return true
return false
}
答案 0 :(得分:2)
在发生不匹配时,一个接一个地比较两个字符串的字符跳过不匹配字符并保持不匹配的计数。如果发生多于1个不匹配,则检查返回false。在最坏的情况下,检查的时间复杂度是O(n)。
def check(a: String, b: String): Boolean = {
val smallerStr = if (a.length < b.length) a else b
val largerStr = if (a.length > b.length) a else b
if (largerStr.length - smallerStr.length > 1) false
else {
def countMismatches(aIndex: Int, bIndex: Int, mismatchCount: Int): Int = {
if (bIndex < largerStr.length && aIndex < smallerStr.length) {
if (smallerStr(aIndex) != largerStr(bIndex)) {
if (mismatchCount > 1) mismatchCount
else countMismatches(aIndex, bIndex + 1, mismatchCount + 1)
}
else countMismatches(aIndex + 1, bIndex + 1, mismatchCount)
} else mismatchCount
}
countMismatches(0, 0, 0) <= 1
}
}
<强> REPL 强>
res12: Boolean = true
@ check("zapple", "apple")
res13: Boolean = true
@ check("apple", "apzple")
res14: Boolean = true
@ check("apple", "apzzple")
res15: Boolean = false
@ check("apple", "applez")
res16: Boolean = true
@ check("apple", "applzz")
res17: Boolean = false
答案 1 :(得分:1)
您可以通过删除额外的s.take(i) + s.substring(i+1)
来加快速度。您可以浏览s并将索引与obj中的对应项进行比较。当您发现差异时,请使用s.take(i) + s.substring(i+1)
。
def check_extra_char(s: String, obj: String): Boolean = {
if(s.length != obj.length+1) return false // Automatically false if obj is not one char smaller than s
if(s.dropRight(1) == obj) return true // so we don't go outOfIndex later
for (i <- 0 until s.length)
if (s(i) != obj(i)){
if (s.take(i) + s.substring(1+i) == obj) return true else return false
return false
}
答案 2 :(得分:1)
您的代码问题是s.take(i) + s.substring(1+i) == obj
部分。 String.take(i: Int)
和String.substring(start: Int, end: Int)
都有O(n)
时间复杂度。
有很多方法可以避免这种情况,我提供其中一个用尾随递归的惯用scala,
import scala.annotation.tailrec
def checkExtraChar(source: String, target: String): Boolean = {
val sourceLength = source.length
val targetLength = target.length
// Assumption :: source.length == target.length + 1
@tailrec
def _check(srcIndex: Int, tgtIndex: Int, mismatchFound: Boolean): Boolean = srcIndex match {
case index if index == (sourceLength - 1) && !mismatchFound => true
case index if index == (sourceLength - 1) => source(srcIndex) == target(tgtIndex)
case _ => (source(srcIndex) == target(tgtIndex), mismatchFound) match {
case (true, _) => _check(srcIndex + 1, tgtIndex + 1, mismatchFound)
case (false, false) => _check(srcIndex + 1, tgtIndex, true)
case (false, true) => false
}
}
(sourceLength == targetLength + 1) match {
case false => false
case true => _check(0, 0, false)
}
}
checkExtraChar("qwerty", "werty") // true
checkExtraChar("wqerty", "werty") // true
checkExtraChar("qqwerty", "werty") // false
答案 3 :(得分:0)
得到的东西应该会产生一些改进,而且更具惯用性。
def check(s: String, obj: String): Boolean = {
if(s.length == obj.length + 1)
streamFromString(s, 0).exists { case(substring) => substring == obj }
else false
}
def streamFromString(s: String, withoutIndex: Int): Stream[String] = {
lazy val next: Stream[String] = if(withoutIndex > s.length - 1) Stream.empty else streamFromString(s, withoutIndex + 1)
s.patch(withoutIndex, Nil, 1) #:: next
}
首先,您可以使用patch
创建没有给定元素的子集合;它是在Seq
上定义的,所以请随意阅读。
其次,我没有盲目地循环并重新发明轮子,而是决定创建一个Stream[String]
创建的补丁,但由于Stream
是一个懒惰的集合,我们将只在我们进行流的过程中创建后续补丁
您可以将exists
来电替换为collectFirst
,以PartialFunction
取得对身体的更多控制权,但在这种情况下,这将是一种矫枉过正的行为,imo。
streamFromString(s, 0)
.collectFirst { case (substring) if substring == obj => "" } //could be anything here really, it's all about getting Option.Some
.fold(false)(_ => true)