我正试图解决一个问题。
问题:你会得到一个有4种颜色的N个球的序列:红色、绿色、黄色和蓝色。当且仅当以下所有条件都成立时,序列充满颜色:
红色的球和绿色的球一样多。黄色的球和蓝色的球一样多。序列的每个前缀中的红色球和绿色球的数量之差最多为1。序列的每个前缀中的黄色球和蓝色球的数量之差最多为1。你的任务是编写一个程序,对于给定的序列,如果它是全彩色的,就会打印True,否则就会打印False。
我的解决方案:对于每个字符串,我生成所有可能的前缀和后缀,以验证条件编号3和4。但这需要更多的时间。
我们可以迭代字符串并验证条件,而不是每次都生成前缀并验证条件。当条件不满足时,我想跳出循环。我无法用功能性的方式来实现这一点。有人能帮我怎么做到吗?
我的解决方案:
object Test {
def main(args: Array[String]) {
def isValidSequence(str: String) = {
def isValidCondition(ch1:Char, ch2:Char, m:Map[Char, Int]):Boolean = m.getOrElse(ch1, 0) - m.getOrElse(ch2, 0) > 1
def groupByChars(s:String) = s.groupBy(ch => ch).map(x => (x._1, x._2.length))
def isValidPrefix(s:String):Boolean = (1 to s.length).exists(x => isValidCondition('R', 'G', groupByChars(s.take(x))))
val x = groupByChars(str)
lazy val cond1 = x.get('R') == x.get('G')
lazy val cond2 = x.get('B') == x.get('Y')
lazy val cond3 = isValidPrefix(str)
lazy val cond4 = isValidPrefix(str.reverse)
cond1 && cond2 && !cond3 && !cond4
}
def printBoolValue(b:Boolean) = if(b) println("True") else println("False")
val in = io.Source.stdin.getLines()
val inSize = in.take(1).next().toInt
val strs = in.take(inSize)
strs.map(isValidSequence(_)).foreach(printBoolValue)
}
}
作为另一个答案,这里有一个更简单的解决方案,它确实缩短了差异检查。
val valid = List("RGYBRGYB")
val invalid = List("RGYBR", "RGYBY", "RGYBY", "RGYYB")
def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
if (current < -1 || current > 1) false
else if (s.length == 0) true
else differenceHelp(s.tail, a, b,
if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
}
differenceHelp(s, a, b, 0)
}
lazy val cond1 = s.count('R'==) == s.count('G'==)
lazy val cond2 = s.count('Y'==) == s.count('B'==)
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond1 && cond2 && cond3 && cond4
}
valid.forall(checkBalls(_)) //> res0: Boolean = true
invalid.forall(!checkBalls(_)) //> res1: Boolean = true
EDIT:作为优化,我们可以将cond1作为cond3的一部分(将cond2作为cond4的一部分(。当且仅当字符串末尾的计数为0时,每个都有相等的数字。只有在这种情况下,我们才能在差异中检查并返回true。因此,
def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
if (current < -1 || current > 1) false
else if (s.length == 0) (count == 0) // <- this line changed
else differenceHelp(s.tail, a, b,
if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
}
differenceHelp(s, a, b, 0)
}
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond3 && cond4
}
它像以前的版本一样通过了测试。通过在对differences
的一次调用中进行R/G和Y/B检查,它可以稍微快一点,但这看起来有点过于专业化了。
如果需要,这里有一个使用流的解决方案。
代码:-
object RGYB extends App {
val validPattern = List(
"RG","RYBG","RYGB","RBGY",
"GR","GYBR","GYRB","GBRY",
"YB","YRGB","YRBG","YGRB",
"BY","BRGY","BRYG","BGYR"
)
val pattern ="RGRG"
pattern.sliding(4).foreach { x1 =>
val count = validPattern.filter { p1 => {
x1.equalsIgnoreCase(p1)
}
}.size
if(count<1)
{
x1.sliding(2).foreach {
x2=>
val counter = validPattern.filter { p2 => {
x2.equalsIgnoreCase(p2)
}
}.size
if(counter<1)
{
println("false !! not valid due to "+x2);
System.exit(0)
}
}
println("false !! not valid due to "+x1);
System.exit(0)
}
}
println("True !!"+pattern+" Is a valid string pattern")
}
因此,诀窍是首先检查最长的前缀。如果失败了,我们就完了。否则,我们使用下一个最长的前缀并递归。如果我们得到空字符串,它会传递所有前缀,因此它是有效的。
def isValidPrefix(s: String): Boolean =
if (s.length == 0)
true
else if (!isValidCondition('R', 'G', groupByChars(s)))
false
else isValidPrefix(s.init)