【问题标题】:how to find longest palindromic subsequence?如何找到最长的回文子序列?
【发布时间】:2011-06-15 00:01:10
【问题描述】:

这是算法书(Vazirani 撰写)中的问题(6.7 ch6),与finding longest palindrome 的经典问题略有不同。我该如何解决这个问题?

一个子序列是回文的,如果它是 无论从左到右读还是一样 右到左。例如, 序列

A,C,G,T,G,T,C,A,A,A,A,T,C,G

有许多回文子序列, 包括A,C,G,C,AA,A,A,A (另一方面,子序列 A,C,T 不是回文)。设计一个 采用序列x[1 ...n] 并返回(长度)的算法 最长的回文子序列。它的 运行时间应该是O(n^2)

【问题讨论】:

  • 我建议你看看这个,这是一篇关于在线性时间内找到最长回文的论文。 (akalin.cx/longest-palindrome-linear-time)
  • 您所说的“子序列”似乎意味着abcxxba 具有abcba 作为最长的回文子序列- 对吗?因为在那种情况下,接受的答案在我看来是错误的......
  • 此处基于 C++ 的解决方案 - stackoverflow.com/a/44542960/1874627

标签: algorithm dynamic-programming palindrome


【解决方案1】:

从给定字符串中查找最长回文子串的程序。

package source;
        
        import java.util.ArrayList;
                
        public class LongestPalindrome 
        {
            //Check the given string is palindrome by 
            public static boolean isPalindrome (String s)
            {
                StringBuffer sb = new StringBuffer(s);
                if(s.equalsIgnoreCase(sb.reverse().toString()))
                    return true;
                else
                    return false;
            }
        
            public static void main(String[] args) 
            {
                //String / word without space
                String str = "MOMABCMOMOM"; // "mom" //"abccbabcd"
                
                if(str.length() > 2 )
                {
                    StringBuffer sb = new StringBuffer();
                    ArrayList<String> allPalindromeList = new ArrayList<>();
                            
                    for(int i=0; i<str.length(); i++)
                    {
                        for(int j=i; j<str.length(); j++)
                        {
                            sb.append(str.charAt(j));
                            if( isPalindrome(sb.toString()) ) {
                                allPalindromeList.add(sb.toString());                       
                            }
                        }
                        //clear the stringBuffer
                        sb.delete(0, sb.length());
                    }
                     
                    int maxSubStrLength = -1;
                    int indexMaxSubStr = -1;
                    int index = -1;
                    
                    for (String subStr : allPalindromeList) {
                        ++index;
                        if(maxSubStrLength < subStr.length()) {
                            maxSubStrLength = subStr.length();
                            indexMaxSubStr = index;
                        }
                    }
                    if(maxSubStrLength > 2)
                        System.out.println("Maximum Length Palindrome SubString is : "+allPalindromeList.get(indexMaxSubStr));
                    else
                        System.out.println("Not able to find a Palindrome who is three character in length!!");
                
                }
            }
        
        }

【讨论】:

    【解决方案2】:
    private static int findLongestPalindromicSubsequence(String string) { 
        int stringLength = string.length();
        int[][] l = new int[stringLength][stringLength];
        for(int length = 1; length<= stringLength; length++){
            for(int left = 0;left<= stringLength - length;left++){
                int right = left+ length -1;
                if(length == 1){
                    l[left][right] = 1;
                }
                else{  
                    if(string.charAt(left) == string.charAt(right)){
                        //L(0, n-1) = L(1, n-2) + 2
                        if(length == 2){
                            // aa
                            l[left][right] = 2;
                        }
                        else{
                            l[left][right] = l[left+1][right-1]+2;
                        } 
                    }
                    else{
                        //L(0, n-1) = MAX ( L(1, n-1) ,  L(0, n-2) )
                        l[left][right] = (l[left+1][right] > l[left][right-1])?l[left+1][right] : l[left][right-1];
                    } 
                }  
            }
        } 
        return l[0][stringLength-1];
    }
    

    【讨论】:

      【解决方案3】:

      导入 java.util.HashSet;

      导入 java.util.Scanner;

      /** * @param 参数 * 我们得到一个字符串,我们需要在该字符串中找到最长的子序列,即回文 * 在这段代码中,我们使用 hashset 来确定给定字符串中唯一的子字符串集 */

      公共类 NumberOfPalindrome {

          /**
           * @param args
           * Given a string find the longest possible substring which is a palindrome.
           */
          public static HashSet<String> h = new HashSet<>();
          public static void main(String[] args) {
              Scanner sc = new Scanner(System.in);
              String s = sc.nextLine();
              for(int i=0;i<=s.length()/2;i++)
                  h.add(s.charAt(i)+"");
              longestPalindrome(s.substring(0, (s.length()/2)+(s.length()%2)));
              System.out.println(h.size()+s.length()/2);
              System.out.print(h);
          }
      
          public static void longestPalindrome(String s){
              //System.out.println(s);
              if(s.length()==0 || s.length()==1)
                  return;
              if(checkPalindrome(s)){
                  h.add(s);
              }
              longestPalindrome(s.substring(0, s.length()-1));
              longestPalindrome(s.substring(1, s.length()));
      
          }
          public static boolean checkPalindrome(String s){
              //System.out.println(s);
              int i=0;int j=s.length()-1;
              while(i<=j){
                  if(s.charAt(i)!=s.charAt(j))
                      return false;
                  i++;j--;
              }
              return true;
          }
      }
      

      【讨论】:

        【解决方案4】:

        这可以使用动态规划在 O(n^2) 内解决。基本上,问题在于使用x[i+1...j]x[i,...j-1]x[i+1,...,j-1] 的最长子序列(如果第一个和最后一个字母相同)在x[i...j] 中构建最长的回文子序列。

        首先,空字符串和单个字符串通常是回文。 请注意,对于子串x[i,...,j],如果x[i]==x[j],我们可以说最长回文的长度是x[i+1,...,j-1]+2 上的最长回文。如果不匹配,则最长回文是x[i+1,...,j]y[i,...,j-1] 中的最大值。

        这给了我们函数:

        longest(i,j)= j-i+1 if j-i<=0,
                      2+longest(i+1,j-1) if x[i]==x[j]
                      max(longest(i+1,j),longest(i,j-1)) otherwise
        

        您可以简单地实现该函数的记忆版本,或者编写一个自下而上的最长 [i][j] 表。

        这只会给你最长子序列的长度,而不是实际的子序列本身。但它也可以很容易地扩展来做到这一点。


        【讨论】:

        • 我认为它们匹配时应该是 2 + ...j-i if j-i&lt;=1
        • 这是一个 O(n^2) 算法吗? N 个不同字符的输入不会导致递归调用的指数增长吗?谁能解释一下?
        • @srbh.kmr:这就是为什么你需要记忆函数,或者自下而上构建表格的原因。表最长[i][j]有O(N^2)个单元格,所以如果每个状态只访问一次,算法是O(N^2)。
        • 我认为你不能说“2+longest(i+1,j-1) if x[i]==x[j]”。如果 x[i+1...j-1] 不是回文,则 x[i]==x[j] 不加最长值,即长度不能加 2。即如果像“ABCA”这样的字符串,x[0]==x[3],但是最长的(0,3)!= 2。
        • 鉴于字符串“GG”,我不明白它是如何工作的。它不会达到第一个条件并返回 1 吗?
        【解决方案5】:

        这个问题也可以作为一个非常常见的问题的变体来完成,称为 LCS(最长公共子序列)问题。 让输入字符串由字符数组 s1[0...n-1] 表示。

        1) 反转给定序列并将反转存储在另一个数组中,例如 s2[0..n-1],本质上是 s1[n-1....0]

        2) 给定序列 s1 和反向序列 s2 的 LCS 将是最长的回文序列。

        这个解也是 O(n^2) 解。

        【讨论】:

        • 很遗憾这是不正确的。例如,对于字符串 ACBAC,ACBAC 及其反向 CABCA 的最长公共子序列是 ABC,但它不是对称的。
        • @uohzxela ACA也是ACBAC的最长公共子序列,其反向CABCA和ACA是对称的
        • 您可以添加一个额外的步骤来检查 LCS 是否对称,这需要 O(n^3) 的最坏情况。
        • X 的每个最长回文子序列也是 X 的最长公共子序列及其逆,但反过来不成立。但是,可以轻松地修改标准 LCS 动态规划算法以返回一个回文 LCS(给定 X 及其相反)。
        【解决方案6】:

        子串和子序列的区别让我有点困惑。(见Ex6.8和6.11) 根据我们对子序列的理解,给出的例子没有回文子序列ACGCA。 这是我的伪代码,我不太确定初始化>

        for i = 1 to n do
            for j = 1 to i-1 do
                L(i,j) = 0
        for i = 1 to n do
            L(i,i) = 1
        for i = n-1 to 1 do    //pay attention to the order when filling the table
            for j = i+1 to n do
                if x[i] = x[j] then
                   L(i,j) = 2 + L(i+1, j-1)
                else do
                   L(i,j) = max{L(i+1, j), L(i, j-1)}
        return max L(i,j)
        

        正在为最终算法做准备……

        【讨论】:

          【解决方案7】:

          最长回文序列的工作 Java 实现

          public class LongestPalindrome 
          {
              int max(int x , int y)
              {
                  return (x>y)? x:y;  
              }
          
              int lps(char[] a ,int i , int j)
              {
                  if(i==j) //If only 1 letter
                  {
                      return 1;
                  }
                  if(a[i] == a[j] && (i+1) == j) // if there are 2 character and both are equal
                  {
                      return 2;   
                  }
                  if(a[i] == a[j]) // If first and last char are equal
                  {
                      return lps(a , i+1 , j-1) +2;
                  }
                  return max(lps(a,i+1 ,j),lps(a,i,j-1)); 
              }
          
              public static void main(String[] args) 
              {
                  String s = "NAMAN IS NAMAN";
                  LongestPalindrome p = new LongestPalindrome();
                  char[] c = s.toCharArray();
                  System.out.print("Length of longest seq is" + p.lps(c,0,c.length-1));           
              }
          }
          

          【讨论】:

          • 这个方案是动态规划方案吗?抱歉,我无法理解动态规划的概念。而这个解决方案的复杂度是 O(n^2)?
          • 是的,这是动态编程方法。这是上面提供的解决方案的简单实现。我不确定复杂性。你也可以制作这个解决方案的记忆版本,因为这个问题有重叠子问题。
          • 不适用于 input = "forgeeksskeegfor" - 错误地说长度是:12,而应该是 10 ("geeksskeeg")。
          • 我有几点要说明:1)这不是动态编程,而是具有指数时间复杂度的天真递归和 2)@javauser71:12 是正确的结果,回文是以下任何一项:“fgeeksskeegf”、“ogeeksskeego”或“rgeeksskeegr”。请记住,子序列不一定是连续的!
          【解决方案8】:

          输入: A1,A2,....,An

          目标:找到最长的严格递增的子序列(不一定是连续的)。

          L(j):以j结尾的最长严格递增子序列

          L(j): max{ L(i)}+1 } where i &lt; j and A[i] &lt; A[j]

          然后找到max{ L(j) } for all j

          你会得到源代码here

          【讨论】:

          • 欢迎您,感谢您的回答。不过,在未来,值得包含代码的详细信息(不仅仅是链接到外部资源)。此外,最上面答案旁边的绿色勾号表示该问题已经有一个已接受的答案。尝试回答尚未引起如此多关注的问题 - 您的贡献将得到更大的重视。
          【解决方案9】:

          对于字符串中的每个字母:

          • 将字母设置为回文的中间(当前长度=1)

          • 如果这是它的中间,检查回文将是多长时间

          • 如果这个回文比我们找到的长(直到现在):保留回文的索引和大小。

          O(N^2) :因为我们有一个选择中间的循环和一个检查回文长度的循环,如果这是中间的话。每个循环从 0 运行到 O(N) [第一个从 0 到 N-1,第二个从 0 到 (N-1)/2]

          例如: D B A B C B A

          i=0 : D 是回文的中间,不能长于 1(因为它是第一个)

          i=1: B 是回文的中间,检查 B 前后的 char : 不相同(一侧为 D,另一侧为 A)--> 长度为 1。

          i=2 :A 是回文的中间,检查 A 前后的字符:B --> 长度均为 3。检查间隙为 2 的字符:不相同(一侧为 D,另一侧为 C) --> 长度为 3。

          等等。

          【讨论】:

          • 该问题要求最长回文子序列,而不是子字符串。这意味着你取的字符串中的字母不需要是连续的。
          猜你喜欢
          • 2012-10-05
          • 2018-05-13
          • 2020-04-14
          • 1970-01-01
          • 2014-05-22
          • 1970-01-01
          • 1970-01-01
          • 2011-09-02
          • 2017-02-25
          相关资源
          最近更新 更多