【问题标题】:repeat string n times in Kotlin在 Kotlin 中重复字符串 n 次
【发布时间】:2018-07-04 21:41:06
【问题描述】:

我想创建一个string,其中包含* 符号n 次。 我只看到这种方式:

val s = ""
val n = 100
for (j in 0 until n) {
    s += "*"
}

但它看起来很难看,而且时间复杂度为 O(n^2)。 Kotlin 有没有一种方法可以在没有具有更好时间复杂度的循环的情况下做到这一点?

【问题讨论】:

  • 这实际上是 O(n²),因为 s += "*" 重新创建了整个字符串(长度为 1 到 n)并添加了一个“*”。
  • 我更正了这个问题,谢谢
  • 解决性能问题使用val s = StringBuilder(n)

标签: kotlin


【解决方案1】:

内置的CharSequence.repeat 扩展以一种有效的方式执行此操作,请参阅源代码here

val str: String = "*".repeat(100)

当然,这仍然需要 O(n) 步骤来创建字符串。但是,使用这个内置的 stdlib 函数有它的优点:它是跨平台的、易于阅读的,并且如果有更有效的解决方案,可以随着时间的推移提高性能。无论如何,其中的循环可能会被编译器或运行时优化。

【讨论】:

  • 看起来不错,谢谢!似乎没有办法比 O(n) 更快,你怎么看?
  • 如果您查看 Java 的同一个问题(例如herehere),所有内容似乎都涉及遍历字符串/数组的长度一次。
  • @Rainmaker 没有办法在小于 O(n) 的时间内将 n 个字符串相加。您将始终需要至少访问每个字符串一次。
【解决方案2】:

CharSequence.repeat 的替代方法是带有 init 函数的 CharArray:

CharArray(N, {i -> '*'}).joinToString(separator="")

此解决方案的优点是您可以定义前缀、后缀和分隔符。

【讨论】:

    【解决方案3】:

    StringBuilder 将改进 memory footprint here:

    val sb = StringBuilder() 
    val n = 100
    for (j in 0 until n) {
      sb.append("*") 
    }
    

    【讨论】:

    • 是的,它更好,谢谢,但它仍然是循环并且仍然是 O(n)
    • O(n) 比原来的 O(n^2) 好,虽然
    • 为什么是 n^2?只有一个循环
    • 字符串是不可变的,因此 n^2 复制每个连接的结果。
    • 您不必实例化 StringBuilder,而是这样做: val result = buildString { (0..100).forEach { append("*") } }
    【解决方案4】:

    感谢Anton Sizikovhttps://kotlinlang.org/api/latest/jvm/stdlib/kotlin/repeat.html,我们可以写:

    val sb = StringBuilder().apply{
        repeat(100) {
            append("*")
        }
    }
    

    但这是一种简单的方法。查看power 函数。它的复杂度为 O(log n)。

    对于StringBuilder

    private fun power(sb: StringBuilder, n: Int): StringBuilder =
        when {
            n == 0 -> StringBuilder("")
            n % 2 == 0 -> {
                val part = power(sb, n / 2)
                part.append(part)
            }
            else -> {
                val part = power(sb, n / 2)
                part.append(part).append(sb)
            }
        }
    

    对于String

    private fun pow(s: String, n: Int): String =
        when {
            n == 0 -> ""
            n % 2 == 0 -> pow(s, n / 2).repeat(2)
            else -> s + pow(s, n / 2).repeat(2)
        }
    

    然后我们可以调用它们:

    // 1.
    val sb1 = StringBuilder().apply {
        repeat(100) {
            append("*")
        }
    }
    
    // 2.
    val sb2 = power(StringBuilder("*"), 100)
    
    // 3.
    val s = power("*", 100)
    
    println(sb1.toString())
    println(s)
    println(sb2.toString())
    

    【讨论】:

      【解决方案5】:

      如果您需要分隔符,List 中的这个初始化函数很有帮助:

      val str: String = List(100) { "*" }.joinToString(",")
      

      【讨论】:

        【解决方案6】:

        您可以重载* 运算符以将其映射到现有的repeat extension

        public operator fun CharSequence.times(count: Int): String {
            return repeat(count)
        }
        
        fun main() {
            val s = "*" * 101
            println(s)
            println("Badger " * 12 + "Mushroom " * 2)
            println(s)
        }
        

        【讨论】:

          猜你喜欢
          • 2013-07-27
          • 2010-09-21
          • 1970-01-01
          • 1970-01-01
          • 2013-10-12
          • 1970-01-01
          • 2019-11-12
          • 2014-04-17
          相关资源
          最近更新 更多