9

CodeChef からこの問題に遭遇しました。問題は次のように述べています。

正の整数は、左から右に読んでも右から左に読んでも、10 進法での表現が同じ場合、回文と呼ばれます。1000000 桁以下の正の整数 K に対して、K より大きい最小の回文の値を出力に書き込んでください。

次のように isPalindrome メソッドを定義できます。

def isPalindrome(someNumber:String):Boolean = someNumber.reverse.mkString == someNumber

私が直面している問題は、整数が isPalindrome メソッドを満たすときに、最初に指定された数値からループしてブレークし、最初の回文を返す方法です。また、isPalindrome メソッドを記述するより良い (効率的な) 方法はありますか?

ここでいくつかのガイダンスを得るのは素晴らしいことです

4

9 に答える 9

5

あなたが知っている123xxxのような番号を持っているなら、どちらかのxxxが321より下でなければならない-そして次の回文は123321です。

または、xxxが上にある場合、3を保持できず、124421が次の1つである必要があります。

これは保証のないコードで、あまりエレガントではありませんが、真ん中の(複数の)ナインの場合は少し毛深いです(19992):

object Palindrome extends App {

def nextPalindrome (inNumber: String): String = {
  val len = inNumber.length ()
  if (len == 1 && inNumber (0) != '9') 
    "" + (inNumber.toInt + 1) else {
    val head = inNumber.substring (0, len/2)
    val tail = inNumber.reverse.substring (0, len/2)
    val h = if (head.length > 0) BigInt (head) else BigInt (0)
    val t = if (tail.length > 0) BigInt (tail) else BigInt (0)

    if (t < h) {
      if (len % 2 == 0) head + (head.reverse)
      else inNumber.substring (0, len/2 + 1) + (head.reverse)
    } else {
     if (len % 2 == 1) {
       val s2 = inNumber.substring (0, len/2 + 1) // 4=> 4
       val h2 = BigInt (s2) + 1  // 5 
       nextPalindrome (h2 + (List.fill (len/2) ('0').mkString)) // 5 + "" 
     } else {
       val h = BigInt (head) + 1
       h.toString + (h.toString.reverse)
     }
    }
  }
}

def check (in: String, expected: String) = {
  if (nextPalindrome (in) == expected) 
    println ("ok: " + in) else 
    println (" - fail: " + nextPalindrome (in) + " != " + expected + " for: " + in)
}
//
val nums = List (("12345", "12421"), // f
  ("123456", "124421"), 
  ("54321", "54345"), 
  ("654321", "654456"), 
  ("19992", "20002"),
  ("29991", "29992"),
  ("999", "1001"),
  ("31", "33"),
  ("13", "22"),
  ("9", "11"),
  ("99", "101"),
  ("131", "141"),
  ("3", "4")
)
nums.foreach (n => check (n._1, n._2))
println (nextPalindrome ("123456678901234564579898989891254392051039410809512345667890123456457989898989125439205103941080951234566789012345645798989898912543920510394108095"))

}

100万桁のIntの場合も処理できると思います。

于 2012-04-23T03:16:34.923 に答える
5

逆にするのは最高のアイデアではありません。文字列の最初と最後から始めて、要素ごとに繰り返して比較することをお勧めします。最初と最後の要素が一致しない場合でも、文字列全体をコピーして元に戻すのに時間を浪費しています。百万桁の何かでは、それは大きな無駄になるでしょう。

これは、数値が大きい場合の逆方向よりも数桁高速です。

def isPalindrome2(someNumber:String):Boolean = {
  val len = someNumber.length;
  for(i <- 0 until len/2) {
    if(someNumber(i) != someNumber(len-i-1)) return false; 
  }
  return true;
}

文字列の前半をミラーリングすることに基づいた、おそらくさらに高速な方法があります。私は今それを得ることができるかどうかを確認します...

更新したがって、これはほぼ一定の時間で次の回文を見つけるはずです。ループはありません。ちょっと引っかいただけなので、片付けられると思います。

def nextPalindrome(someNumber:String):String = {
  val len = someNumber.length;
  if(len==1) return "11";
  val half = scala.math.floor(len/2).toInt;
  var firstHalf = someNumber.substring(0,half);
  var secondHalf = if(len % 2 == 1) {
    someNumber.substring(half+1,len);
  } else {
    someNumber.substring(half,len);
  }

  if(BigInt(secondHalf) > BigInt(firstHalf.reverse)) {
    if(len % 2 == 1) {
      firstHalf += someNumber.substring(half, half+1);
      firstHalf = (BigInt(firstHalf)+1).toString;
      firstHalf + firstHalf.substring(0,firstHalf.length-1).reverse
    } else {
      firstHalf = (BigInt(firstHalf)+1).toString;
      firstHalf + firstHalf.reverse;
    }
  } else {
    if(len % 2 == 1) {
      firstHalf + someNumber.substring(half,half+1) + firstHalf.reverse;
    } else {
      firstHalf + firstHalf.reverse;
    }
  }
}
于 2012-04-23T03:16:57.830 に答える
3

これは私が達成できる最も一般的で明確な解決策です
:BigInt

def incStr(num: String) = {  // helper method to increment number as String
  val idx = num.lastIndexWhere('9'!=, num.length-1)
  num.take(idx) + (num.charAt(idx)+1).toChar + "0"*(num.length-idx-1)
}

def palindromeAfter(num: String) = {
  val lengthIsOdd = num.length % 2 
  val halfLength  = num.length / 2 + lengthIsOdd
  val leftHalf  = num.take(halfLength)               // first half of number (including central digit)
  val rightHalf = num.drop(halfLength - lengthIsOdd) // second half of number (also including central digit)      

  val (newLeftHalf, newLengthIsOdd) =  // we need to calculate first half of new palindrome and whether it's length is odd or even
    if (rightHalf.compareTo(leftHalf.reverse) < 0) // simplest case - input number is like 123xxx and xxx < 321
      (leftHalf, lengthIsOdd) 
    else if (leftHalf forall ('9'==))              // special case - if number is like '999...', then next palindrome will be like '10...01' and one digit longer
      ("1" + "0" * (halfLength - lengthIsOdd), 1 - lengthIsOdd)
    else                                           // other cases - increment first half of input number before making palindrome
      (incStr(leftHalf), lengthIsOdd)

  // now we can create palindrome itself
  newLeftHalf + newLeftHalf.dropRight(newLengthIsOdd).reverse
}   
于 2012-04-23T07:16:54.117 に答える
1

範囲のない提案によると:同じことですが、ストリームを使用します:

def isPalindrome(n:Int):Boolean = n.toString.reverse == n.toString
def ints(n: Int): Stream[Int] = Stream.cons(n, ints(n+1))
val result = ints(100).find(isPalindrome)

そして、イテレータ(および異なる呼び出しメソッド、実際にはStreamで実行できるのと同じこと)を使用します。

val result = Iterator.from(100).find(isPalindrome)

しかし、@ user unknownが述べたように、それは直接的なブルートフォースであり、多数の場合は実用的ではありません。

于 2012-04-23T03:13:22.803 に答える
1

任意のタイプのリストがループなしでスライスを使用して回文であるかどうかを確認するには

def palindrome[T](list: List[T]): Boolean = {
if(list.length==1 || list.length==0 ){
  false
}else {
  val leftSlice: List[T] = list.slice(0, list.length / 2)
  var rightSlice :List[T]=Nil
  if (list.length % 2 != 0) {
    rightSlice = list.slice(list.length / 2 + 1, list.length).reverse
  } else {
    rightSlice = list.slice(list.length / 2, list.length).reverse
  }
  leftSlice ==rightSlice
}

}

最も簡単な解決策は

     def palindrome[T](list: List[T]): Boolean = {
      list == list.reverse
      }
于 2016-11-02T17:25:05.603 に答える
0

コレクションに対してメソッドを使用するだけfindで、特定の述語に一致する最初の要素を見つけることができます。

  def isPalindrome(n:Int):Boolean = n.toString.reverse == n.toString
  val (start, end) = (100, 1000)
  val result: Option[Int] = (start to end).find(isPalindrome)
  result foreach println

  >Some(101)
于 2012-04-23T02:49:02.813 に答える
0

文字列が回文かどうかを確認するソリューション

このソリューションは文字列を逆にしません。ただし、それがより高速になるかどうかはわかりません。

def isPalindrome(s:String):Boolean = { 
  s.isEmpty ||   
  ((s.last == s.head) && isPalindrome(s.tail.dropRight(1)))
}

文字列を指定して次の回文を見つけるソリューション

このソリューションは、scala には最適ではありませんが (Java ソリューションとほぼ同じです)、文字列のみを扱い、大きな数に適しています

必要な数値の前半をミラーリングし、それが開始番号よりも大きいかどうかを確認するだけです。それ以外の場合は、前半の最後の桁を 1 増やして、再度ミラーリングします。

まず、int の文字列表現を 1 ずつインクリメントする関数:

def incrementString(s:String):String = {
  if(s.nonEmpty){
    if (s.last == '9')
      incrementString(s.dropRight(1))+'0'
    else
      s.dropRight(1) + (s.last.toInt +1).toChar
  }else
    "1"
}

次に、int の文字列表現と比較する関数: (その場合、関数「比較」は機能しません)

/* is less that 0 if x<y, is more than 0 if x<y, is equal to 0 if x==y */
def compareInts(x:String, y:String):Int = {
  if (x.length !=y.length)
    (x.length).compare(y.length)
  else
    x.compare(y)
}

次に、次の回文を計算する関数:

def nextPalindrome(origin_ :String):String = {
  /*Comment if you want to have a strictly bigger number, even if you already have a palindrome as input */
  val origin = origin_
  /* Uncomment if you want to have a strictly bigger number, even if you already have a palindrome as input */
  //val origin = incrementString(origin_)

  val length = origin.length
  if(length % 2 == 0){
    val (first, last) = origin.splitAt(length/2); 
    val reversed = first.reverse
    if (compareInts(reversed,last) > -1)
      first ++ reversed
    else{
      val firstIncr = incrementString(first)
      firstIncr ++ firstIncr.reverse
    }
  } else {
    val (first,last) = origin.splitAt((length+1)/2)
    val reversed = first.dropRight(1).reverse
    if (compareInts(reversed,last) != -1)
      first ++ reversed
    else{
      val firstIncr = incrementString(first)
      firstIncr ++ firstIncr.dropRight(1).reverse
    }
  }
}
于 2012-04-23T08:24:31.570 に答える