Ruby - 寻找字符串中最长的回文子串

问题描述 投票:0回答:5

我知道如何找到一个字符串是否是回文

string1 == string1.reverse

虽然字符串中有多个回文,但有点困难

"abcdxyzyxabcdaaa"

上述字符串中,长度大于1的回文有4个

"xyzyx", "yzy", "aaa" and "aa"

在这种情况下,最长的回文是“xyxyx”,长度为5个字符。

我将如何着手解决这个问题。

我知道 array#combination 方法,但在这种情况下不起作用。

我正在考虑实施这样的事情

def longest_palindrome(string)
  palindromes = []
  for i in 2..string.length-1
    string.chars.each_cons(i).each {|x|palindromes.push(x) if x == x.reverse}
  end
  palindromes.map(&:join).max_by(&:length)
end
ruby string substring reverse palindrome
5个回答
3
投票

如果你只是在寻找最大的回文子串,这是一个快速而肮脏的解决方案。

def longest_palindrome(string, size)
  string.size.times do |start| # loop over the size of the string
    break if start + size > string.size # bounds check

    reverse = string[start, size].reverse

    if string.include? reverse #look for palindrome
      return reverse #return the largest palindrome
    end
  end
  longest_palindrome(string, size - 1) # Palindrome not found, lets look for the next smallest size 
end

2
投票
def longest_palindrome(string)
  longest = ''
  i = 0
  while i < string.length
    j = 1
    while (i + j) <= string.length
      x = string.slice(i, j)
      if (x.length > longest.length) && (x == x.reverse)
        longest = x
      end
      j += 1
    end
    i += 1
  end
  longest
end

slice 方法可以很方便地解决这个问题。使用经典的双 while 循环方法测试每个子字符串,

(i, j)
分别代表子字符串的起始索引和长度。
string.slice(start_index, substring_length)

String#slice 方法是这样工作的:

"bdehannahc".slice(3, 8) == "hannah" # which is a palindrome and would be 
                                     # found by the method introduced above

1
投票

这会检查整个字符串

str
是否是回文。如果是,我们就结束了;如果不是,检查所有长度为
str.size-1
的子串。如果一个是回文,我们就完了;如果不是,检查长度为
str.size-1
的子串,依此类推。

def longest_palindrome(str)
  arr = str.downcase.chars
  str.length.downto(1) do |n|
    ana = arr.each_cons(n).find { |b| b == b.reverse }
    return ana.join if ana
  end
end

longest_palindrome "abcdxyzyxabcdaaa"
  #=> "xyzyx"
longest_palindrome "abcdefghba"
  #=> "a"

这里的关键方法是Enumerable#each_cons.


1
投票

最长回文子串:Ruby O(n) Manacher 算法

的解决方案

附上Benchmark

# @param {String} s
# @return {String}
def longest_palindrome(s)
  return "" if s.empty?

  chars = s.chars.zip(s.size.times.map { "|" }).flatten.unshift("|")
  n = chars.size

  p_len = Array.new(3, n)
  p_len[0] = 0
  p_len[1] = 1

  max_len, max_len_pos = 0, 0

  center = 1
  right = 2

  2.step(n - 1).each do |i|   # want to use enumerator; n.times.drop(2) makes (n-2) length array
    mirror = 2 * center - i
    diff = right - i

    expand = false

    if 0 < diff
      len = p_len[mirror]

      if len < diff
        p_len[i] = len

      elsif len == diff && i == n - 1
        p_len[i] = len

      elsif len == diff && i < n - 1
        p_len[i] = len
        expand = true

      elsif diff < len
        p_len[i] = diff
        expand = true

      end
    else
      p_len[i] = 0
      expand = true
    end

    if expand
      while (i + p_len[i]) < n && 0 < (i - p_len[i]) && (not_boundary_char(p_len, i) || same_char?(chars, p_len, i))
        p_len[i] += 1
      end
    end

    if max_len < p_len[i]
      max_len = p_len[i]
      max_len_pos = i
    end

    if i < i + p_len[i]
      center = i
      right = i + p_len[i]
    end
  end

  first = (max_len_pos - max_len) / 2
  last = first + max_len - 1
  s[first..last]
end

def not_boundary_char(p_len, i)
  (i + p_len[i] + 1) % 2 == 0
end

def same_char?(chars, p_len, i)
  chars[i - p_len[i] - 1] == chars[i + p_len[i] + 1]
end

def longest_palindrome1(s)
  arr = s.chars
  s.length.downto(1) do |n|
    ana = arr.each_cons(n).find { |b| b == b.reverse }
    return ana.join if ana
  end
end

# ********************#
#     Benchmark       #
# ********************#

require "benchmark"

s = "anugnxshgonmqydttcvmtsoaprxnhpmpovdolbidqiyqubirkvhwppcdyeouvgedccipsvnobrccbndzjdbgxkzdbcjsjjovnhpnbkurxqfupiprpbiwqdnwaqvjbqoaqzkqgdxkfczdkznqxvupdmnyiidqpnbvgjraszbvvztpapxmomnghfaywkzlrupvjpcvascgvstqmvuveiiixjmdofdwyvhgkydrnfuojhzulhobyhtsxmcovwmamjwljioevhafdlpjpmqstguqhrhvsdvinphejfbdvrvabthpyyphyqharjvzriosrdnwmaxtgriivdqlmugtagvsoylqfwhjpmjxcysfujdvcqovxabjdbvyvembfpahvyoybdhweikcgnzrdqlzusgoobysfmlzifwjzlazuepimhbgkrfimmemhayxeqxynewcnynmgyjcwrpqnayvxoebgyjusppfpsfeonfwnbsdonucaipoafavmlrrlplnnbsaghbawooabsjndqnvruuwvllpvvhuepmqtprgktnwxmflmmbifbbsfthbeafseqrgwnwjxkkcqgbucwusjdipxuekanzwimuizqynaxrvicyzjhulqjshtsqswehnozehmbsdmacciflcgsrlyhjukpvosptmsjfteoimtewkrivdllqiotvtrubgkfcacvgqzxjmhmmqlikrtfrurltgtcreafcgisjpvasiwmhcofqkcteudgjoqqmtucnwcocsoiqtfuoazxdayricnmwcg"
Benchmark.bm do |x|
  x.report("longest_palindrome: ") { longest_palindrome(s) }
  x.report("longest_palindrome1: ") { longest_palindrome1(s) }
end

# user     system      total        real
# longest_palindrome:   0.007118   0.000000   0.007118 (  0.007215)
# longest_palindrome1:   0.433382   0.055217   0.488599 (  0.512605)

-1
投票

这是另一个解决方案,使用 Ruby 的较少特性和迭代而不是递归:

def longest_palindrome(string)
  # to find the longest palindrome, start with whole thing
  substr_start = 0
  substr_length = string.length
  while substr_length > 0 # 1 is a trivial palindrome and the end case
#    puts 'substr_length is:' + substr_length.to_s
    while substr_start <= string.length - substr_length
#      puts 'start is: ' + substr_start.to_s
      if palindrome?(string.slice(substr_start,substr_length))
        puts 'found palindrome: ' + string.slice(substr_start,substr_length)
        return string.slice(substr_start,substr_length)
      end
      substr_start += 1
    end
    substr_start = 0 # inner loop ctr reset
    substr_length -= 1
  end
  puts 'null string tested?'
  return ''
end
© www.soinside.com 2019 - 2024. All rights reserved.