【问题标题】:How to check for palindrome in Swift using recursive definition如何使用递归定义在 Swift 中检查回文
【发布时间】:2015-12-28 07:39:50
【问题描述】:

我喜欢 Swift 中的许多功能,但使用操作字符串仍然是一件很头疼的事情。

func checkPalindrome(word: String) -> Bool {
    print(word)
    if word == "" {
        return true
    } else {
        if word.characters.first == word.characters.last {
            return checkPalindrome(word.substringWithRange(word.startIndex.successor() ..< word.endIndex.predecessor()))
        } else {
            return false
        }
    }
}

只要字符串的长度是奇数,这段代码就会惨遭失败。当然我可以做到,所以块的第一行是if word.characters.count &lt; 2,但是在 Swift 中有没有办法获取子字符串并轻松检查?

更新 我喜欢其中的许多建议,但我想最初的问题可能有点误导,因为这是一个关于 String 的问题,而不是为函数获得正确的结果。

例如,在 Python 中, checkPalindrome(word[1:-1]) 可以很好地用于递归定义,而 Swift 代码则不那么优雅,因为它需要其他的花里胡哨。

【问题讨论】:

  • 你可以检查 String(characters.reverse()) == self
  • 检查 word.characters.count % 2 == 1 如果为真则删除中心的字符
  • 在 Swift 中操作字符串不是“头疼”!
  • 它“惨遭失败”,因为 Swift 不允许使用 startIndex>endIndex 创建范围。
  • 其实我前几天学到的是,你可以像这样轻松地将字符串转换为数组。 var stringArray = Array(string)。这将轻松地将任何字符串转换为数组。这当然是使用swift,但你明白我的意思。然后要更改字符串,只需使用 stringArray[0] = stringArray[1] 即可互换这两个字母。和wallah,你现在已经切换了角色

标签: swift recursion palindrome


【解决方案1】:
return word == String(word.reversed())

【讨论】:

    【解决方案2】:
    func isPalindrome(myString:String) -> Bool {
        let reverseString = String(myString.characters.reversed())
        if(myString != "" && myString == reverseString) {
            return true
        } else {
            return false
        }
    }
    
    print(isPalindrome("madam"))
    

    【讨论】:

    • 不幸的是,这个答案不再相关,因为String.characters 在 Swift 3.2 中已被弃用。
    • @RostyslavDruzhchenko,您可以将String(myString.characters.reversed()) 替换为myString.reversed() 就完成了。
    【解决方案3】:

    我已经使用下面的扩展来查找号码是否是回文。

    extension String {
        var isPalindrome: Bool {
            return self == String(self.reversed())
        } 
     }
    

    【讨论】:

      【解决方案4】:

      有时有一个递归的前端可以简化生活。当最方便使用的参数不是我想要的用户界面时,我有时会这样做。

      以下内容能满足您的需求吗?

      func checkPalindrome(str: String) -> Bool {
        func recursiveTest(var charSet: String.CharacterView) -> Bool {
          if charSet.count < 2 {
            return true
          } else {
            if charSet.popFirst() != charSet.popLast() {
              return false
            } else {
              return recursiveTest(charSet)
            }
          }
        }
        return recursiveTest(str.characters)
      }
      

      【讨论】:

        【解决方案5】:

        只需在 if 中添加更多条件

                func checkPalindrome(word: String) -> Bool {
                print(word)
            if (word == "" || word.characters.count == 1){
                    return true
        
                }
            else {
                    if word.characters.first == word.characters.last {
                        return checkPalindrome(word.substringWithRange(word.startIndex.successor() ..< word.endIndex.predecessor()))
                    } else {
                        return false
                    }
                }
            }
        

        【讨论】:

          【解决方案6】:
          extension StringProtocol where Self: RangeReplaceableCollection {
              var letters: Self { filter(\.isLetter) }
              var isPalindrome: Bool {
                  let letters = self.letters
                  return String(letters.reversed()).caseInsensitiveCompare(letters) == .orderedSame
              }
          }
          

          "Dammit I'm Mad".isPalindrome    // true
          "Socorram-me subi no onibus em marrocos".isPalindrome   // true
          

          您还可以将字符串分解为一个字符数组并遍历它们,直到它的一半与对应的字符逐一比较:


          func checkPalindrome(_ word: String) -> Bool {
              let chars = Array(word.letters.lowercased())
              for index in 0..<chars.count/2 {
                  if chars[index] != chars[chars.count - 1 - index] {
                      return false
                  }
              }
              return true
          }
          

          而递归版本修复了无法用 endIndex 形成范围的范围问题


          func checkPalindrome<T: StringProtocol>(_ word: T) -> Bool {
              let word = word.lowercased()
                  .components(separatedBy: .punctuationCharacters).joined()
                  .components(separatedBy: .whitespacesAndNewlines).joined()
              if word == "" || word.count == 1 {
                  return true
              } else {
                  if word.first == word.last {
                      let start = word.index(word.startIndex,offsetBy: 1, limitedBy: word.endIndex) ?? word.startIndex
                      let end = word.index(word.endIndex,offsetBy: -1, limitedBy: word.startIndex) ?? word.endIndex
                      return checkPalindrome(word[start..<end])
                  } else {
                      return false
                  }
              }
          }
          

          checkPalindrome("Dammit I'm Mad")
          

          【讨论】:

          • 很好的答案,但 OP 似乎想要一个递归解决方案,也许是一个练习。
          • 问题更多的是如何获取子字符串,而不是如何检查字符串是否为回文
          • @WoominJoshPark “但是 Swift 中有没有办法获取子字符串并轻松检查?”你也应该看看这个链接stackoverflow.com/a/34281720/2303865
          【解决方案7】:

          我认为如果你像这样对 String 进行扩展,那么它会让你的生活更轻松:

          extension String {
              var length: Int { return characters.count }
          
              subscript(index: Int) -> Character {
                  return self[startIndex.advancedBy(index)]
              }
          
              subscript(range: Range<Int>) -> String {
                  return self[Range<Index>(start: startIndex.advancedBy(range.startIndex), end: startIndex.advancedBy(range.endIndex))]
              }
          }
          

          有了它,您可以将您的功能更改为:

          func checkPalindrome(word: String) -> Bool {
              if word.length < 2 {
                  return true
              } 
          
              if word.characters.first != word.characters.last {
                  return false
              }
          
              return checkPalindrome(word[1..<word.length - 1])
          }
          

          快速测试:

          print(checkPalindrome("aba")) // Prints "true"
          print(checkPalindrome("abc")) // Prints "false"
          

          【讨论】:

          • 很好的答案,但我们仍在检查字长。它不像 Python 中的 word[1:-1] 那样优雅地工作......
          【解决方案8】:
          extension String {
          
              func trimmingFirstAndLastCharacters() -> String {
                  guard let startIndex = index(self.startIndex, offsetBy: 1, limitedBy: self.endIndex) else {
                      return self
                  }
          
                  guard let endIndex = index(self.endIndex, offsetBy: -1, limitedBy: self.startIndex) else {
                      return self
                  }
          
                  guard endIndex >= startIndex else {
                      return self
                  }
          
                  return String(self[startIndex..<endIndex])
              }
          
              var isPalindrome: Bool {
                  guard count > 1 else {
                      return true
                  }
          
                  return first == last && trimmingFirstAndLastCharacters().isPalindrome
              }
          }
          

          我们首先声明一个从字符串中删除第一个和最后一个字符的函数。

          接下来我们声明一个计算机属性,它将包含检查字符串是否为回文的实际递归代码。

          如果字符串的大小小于或等于 1,我们立即返回 true(由一个字符组成的字符串,如 "a" 或空字符串 "" 被视为回文),否则我们检查是否第一个和最后一个字符字符串是相同的,我们在当前字符串上递归调用isPalindrome,去掉第一个和最后一个字符。

          【讨论】:

            【解决方案9】:

            将字符串转换为数组。当循环执行时,获取第一个索引并与最后一个索引进行比较。

            func palindrome(string: String)-> Bool{
                let char = Array(string)
                for i in 0..<char.count / 2 {
                    if char[i] != char[char.count - 1 - i] {
                        return false
                    }
                }
                return true
            }
            

            【讨论】:

              【解决方案10】:

              并没有真正想到这个,但我想我想出了一个非常酷的扩展,并想分享。

              extension String {
                  var subString: (Int?) -> (Int?) -> String {
                      return { (start) in
                          { (end) in
                              let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                              let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)
              
                              return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
                          }
                      }
                  }
              }
              
              
              let test = ["Eye", "Pop", "Noon", "Level", "Radar", "Kayak", "Rotator", "Redivider", "Detartrated", "Tattarrattat", "Aibohphobia", "Eve", "Bob", "Otto", "Anna", "Hannah", "Evil olive", "Mirror rim", "Stack cats", "Doom mood", "Rise to vote sir", "Step on no pets", "Never odd or even", "A nut for a jar of tuna", "No lemon, no melon", "Some men interpret nine memos", "Gateman sees name, garageman sees nametag"]
              
              func checkPalindrome(word: String) -> Bool {
                  if word.isEmpty { return true }
                  else {
                      if word.subString(nil)(1) == word.subString(-1)(nil) {
                          return checkPalindrome(word.subString(1)(-1))
                      } else {
                          return false
                      }
                  }
              }
              
              for item in test.map({ $0.lowercaseString.stringByReplacingOccurrencesOfString(",", withString: "").stringByReplacingOccurrencesOfString(" ", withString: "") }) {
                  if !checkPalindrome(item) {
                      print(item)
                  }
              }
              

              【讨论】:

                【解决方案11】:

                此解决方案不是递归的,但它是基于 O(n) 纯索引的解决方案,无需过滤任何内容,也无需创建新对象。非字母字符也会被忽略。

                它使用两个索引并从两侧向外走。

                我承认扩展类型和属性名称是从 Leo 那里窃取的,我很抱歉。 ?

                extension StringProtocol where Self: RangeReplaceableCollection {
                    var isPalindrome : Bool {
                        if isEmpty { return false }
                        if index(after: startIndex) == endIndex { return true }
                        var forward = startIndex
                        var backward = endIndex
                        while forward < backward {
                            repeat { formIndex(before: &backward) } while !self[backward].isLetter
                            if self[forward].lowercased() != self[backward].lowercased() { return false }
                            repeat { formIndex(after: &forward) } while !self[forward].isLetter
                        }
                        return true
                    }
                }
                

                【讨论】:

                  【解决方案12】:

                  Swift 中的一个简单解决方案:

                  func isPalindrome(word: String) -> Bool {
                      
                      // If no string found, return false
                      if word.count == 0 { return false }
                      
                      var index = 0
                      var characters = Array(word) // make array of characters
                      while index < characters.count / 2 { // repeat loop only for half length of given string
                          if characters[index] != characters[(characters.count - 1) - index] {
                              return false
                          }
                          index += 1
                      }
                      
                      return true
                  }
                  

                  【讨论】:

                    【解决方案13】:
                    func checkPalindrome(_ inputString: String) -> Bool {
                        if inputString.count % 2 == 0 {
                            return false
                        } else if inputString.count == 1 {
                            return true
                        } else {
                            var stringCount = inputString.count
                            while stringCount != 1 {
                                if inputString.first == inputString.last {
                                    stringCount -= 2
                                } else {
                                    continue
                                }
                            }
                            if stringCount == 1 {
                                return true
                            } else {
                                return false
                            }
                        }
                    }
                    

                    【讨论】:

                    • let inputString0 = "aabaa" checkPalindrome(inputString0) let inputString1 = "abcuabc" checkPalindrome(inputString1) let inputString2 = "abac" checkPalindrome(inputString2)
                    • 请尝试在您的代码中加入解释,因为这样可以得到更好的答案。
                    • 你的函数为字符串“abba”(这是一个回文)返回false,它为字符串“abcda”(不是回文)返回true,并且它永远不会终止对于字符串“abcde”。 – 问题也是关于递归和子字符串的,没有任何解释的纯代码答案对未来的读者毫无用处。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-03-30
                    • 1970-01-01
                    • 2019-01-05
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多