【问题标题】:Convert string to palindrome with fewest number of insertions将字符串转换为插入次数最少的回文
【发布时间】:2019-08-06 07:13:40
【问题描述】:

这是来自https://www.dailycodingproblem.com/的问题:

给定一个字符串,找出可以通过插入 在单词中的任何位置尽可能少的字符。如果有 是不止一个可以产生的最小长度回文,返回 字典上最早的一个(按字母顺序排列的第一个)。

例如,给定字符串“race”,您应该返回“ecarace”, 因为我们可以向它添加三个字母(这是最小的数量 做回文)。还有其他七个回文可以制作 来自“race”,添加三个字母,但“ecarace”排在第一位 按字母顺序。

作为另一个例子,给定字符串“google”,你应该返回 “elgoogle”。

类似于this SO question,或thisGeeksforGeeks 帖子。相似,但不一样;他们都没有为重复提供任何解释,就好像他们凭空提取了解决方案,并且他们没有重建解决方案,更不用说字典上最早的解决方案了。

经过一番思考,我的理解如下:

观察任何字符串s[i..j],如果s[i] == s[j],那么 使其成为回文所需的插入次数与 使s[i+1..j-1] 成为回文所需的插入次数。

但是,如果 s[i] != s[j],那么我们可以将 s[i..j-1] 转换为 回文然后在开头插入s[j],或者转换 s[i+1..j] 到回文并在末尾插入 s[i]。既然我们是 寻找最少的插入次数,我们将选择 两个选项中的最小值。插入次数多于 1 所选子问题所需的插入次数(对于 在开头或结尾添加一个字符)。

我如何重建字典序上最早的解决方案?

【问题讨论】:

    标签: string algorithm dynamic-programming palindrome


    【解决方案1】:

    首先,让我们回答“我如何重建解决方案”,然后专注于排序。假设您将插入次数存储在 2D 矩阵 insertions[start][stop] 中,您只需要追溯您的步骤,“收集”您插入的字符。我们需要一个新数组来存储输出字符串,长度等于我们的起始字符串加上最小插入次数。我们还将存储两个索引,指向数组前面和后面的下一个可用点。

    首先比较当前子字符串的第一个和最后一个字母,如果相等,则分别在前面和后面的下一个可用位置分配输出字符串。例如,如果我们将FYRF 作为当前子字符串,我们将分配我们的输出字符串F..F,其中. 是未确定的字符。然后我们的子字符串变为s[i+1..j-1]YR

    如果这两个字符不匹配,我们将比较insertions[i+1][j]insertions[i][j-1] 中的记录,看看哪个更小(其中至少有一个正好比insertions[i][j] 小一)。如果它们相等,只需选择一个(我们稍后再讨论)。在输出字符串中分配与我们复制/插入的子字符串的字母相对应的字符,在输出字符串的下一个可用的前后索引处。也就是说,在JLL 的情况下,如果我们决定为JLLJ 添加J,我们将采用子字符串s[i+1..j],因此我们将在输出字符串中存储JJ J..J。如果我们的输出字符串已经包含AR....RA,我们将存储ARJ..JRA。我们重复整个过程,直到所有字符都分配完毕。

    现在,让它按字典顺序排列。关于上一段中insertions[i+1][j]insertions[i][j-1] 相等的情况,我们不应该随意选择其中一个。相反,我们应该按字典顺序比较s[i]s[i+1],如果s[i] 先出现,则将s[i] 插入输出字符串/继续insertions[i+1][j]。否则,使用s[i+1] / insertions[i][j-1]。这将为我们提供所有可用选项中按字典顺序最快的字符串。

    【讨论】:

    • @Dillion Davis,我认为这里的最后一段有一个错字:我们应该比较 s[i] 和 s[j] in s[i......j] 如果s[i] 先出现,将 s[i] 插入输出字符串/继续插入 [i+1][j]。否则,使用 s[j] 并继续插入 [i][j-1]。
    • @rawat 所以你说i+1 应该是j
    • 是的,我的意思是让我们对这个案例说:“种族”我们需要在它的开头插入即“eca”,以使其成为 lexo 最早的回文:s[i..j] = "种族”,s[i](即'r')不等于s[j]('e')和inserts[i+1][j] ==inserts[i][j-1](即2 ) 但是 'r' > 'e' 所以在 s[i] 之前插入 'e' 并继续 s[i.....j-1] 并且 str 现在变成了“erace”。
    【解决方案2】:

    这里的操作:@dillon-davis 的回答是正确的(赞成),尽管那时我自己已经想通了。我已经在问题中提供了基本算法的解释,@dillon-davis 提供了重建的解释,为了完整起见,这里是 Scala 中的工作代码。

    def makePalindromeByFewestEdits(word: String): String = {
        val n = word.length
        val dp = Array.ofDim[Int](n, n)
    
        for (window <- 1 until n)
          (0 until n)
            .map(start => (start, start + window))
            .takeWhile(_._2 < n)
            .foreach {
              case (start, end) if word(start) == word(end) =>
                dp(start)(end) = dp(start + 1)(end - 1)
              case (start, end) =>
                dp(start)(end) = math.min(dp(start + 1)(end), dp(start)(end - 1)) + 1
            }
    
        val minInsertions = dp(0)(n - 1)
        val palindrome = Array.ofDim[Char](n + minInsertions)
    
        @tailrec
        def reconstruct(start: Int, end: Int, count: Int, offset: Int): String = {
          if (count == 0) {
            // we have written 'start' characters from the beginning, the current insertion index is 'offset', and
            // the number of characters left to be written are the substring word[start..end]
            Array.copy(word.toCharArray, start, palindrome, offset, end - start + 1)
            palindrome.mkString
          } else {
            val (s, e, c, ch) = if (word(start) == word(end))
              (start + 1, end - 1, count, word(start))
            else if (dp(start + 1)(end) < dp(start)(end - 1) ||
              (dp(start + 1)(end) == dp(start)(end - 1) && word(start) < word(end))
            )
              (start + 1, end, count - 1, word(start))
            else
              (start, end - 1, count - 1, word(end))
    
            palindrome(offset) = ch
            palindrome(palindrome.length - 1 - offset) = ch
            reconstruct(s, e, c, offset + 1)
          }
        }
    
        reconstruct(0, n - 1, minInsertions, 0)
    }
    

    【讨论】:

      猜你喜欢
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-19
      • 2011-06-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多