4

I was looking at a Perl code golf page (don't ask why) and came across this:

Hole 3 - Smallest Repeating Pattern

Write a subroutine that accepts a string which may consist of a repeating pattern, and returns the smallest repeating substring. If the string does not consist of a repeating pattern, the subroutine should return undef or the empty string. e.g.:

input    output 
'aaaaaa' 'a' 
'ababab' 'ab' 
'aabaab' 'aab' 
'ababaa' ''

Apparently in Perl this can be expressed as sub g3 { pop=~/^(.*?)\1+\z/s&&$1 }

I don't know much Perl so I don't understand how this works. What is the best we can do in Scala? I'm more interested in elegance than the precise number of characters.

Here is my attempt, but it's pretty ugly, which is why I'm asking.

def srp(s: String) =
  s.inits.toList.tail.init.map(i => (i * (s.size / i.size), i)).
    filter(_._1 == s).map(_._2).reverse.headOption.getOrElse("")
Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180

3 Answers3

7

Elegance is subjective...

def smallestPat(input: String) = {
   (1 to input.length).view
      .map(i => input.take(i))
      .find{p => 
        Iterator.iterate(p)(_ + p)
          .takeWhile(_.length <= input.length)
          .exists(_ == input) && p != input}
      .getOrElse("")
}

List("aaaaaa", "ababab", "aabaab", "ababaa") map smallestPat
// res13: List[String] = List(a, ab, aab, "")

Edit and re-edited: slighty shorter:

def smallestPat(i: String) = {
   (1 to i.length - 1)
      .map(i.take)
      .find(p => p * (i.length / p.length) == i)
      .getOrElse("")
}

One more, using grouped:

def smallestPat(i: String) = {
  (1 to i.size/2).map(i.take)
  .find(p => i.grouped(p.size) forall(p==))
  .getOrElse("")
}
huynhjl
  • 41,520
  • 14
  • 105
  • 158
7

Are you willing to admit a Regex-based solution?

def srp(s: String) = {
  val M = """^(.*?)\1+$""".r
  s match {
    case M(m) => Some(m)
    case _ => None
  }
}

Or a one-liner:

val srp = """^(.*?)\1+$""".r.findFirstMatchIn(_: String).map(_.group(1))

Not as concise as the Perl, but I find both considerably more readable.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
2

This is the equivalent Scala one-liner:

"""(?:^(.*?)\1+$)|.*""".r replaceAllIn (_: String, "$1")
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681