【问题标题】:How to find the longest palindromic subsequence (not its length)如何找到最长的回文子序列(不是它的长度)
【发布时间】:2012-10-05 06:24:19
【问题描述】:

我想找出字符串中最长的回文子序列。我到处都能找到找出子序列长度的算法,并声明该算法也可以扩展以返回子序列,但我无处可寻。谁能解释我怎样才能得到序列?

【问题讨论】:

标签: algorithm dynamic-programming


【解决方案1】:

由于您在 geeksforgeeks 中提到了链接Longest Palindromic Subsequence,因此我修改了解决方案以输出结果。我想我们需要一个辅助的二维数组来存储回文子序列是怎么来的,所以最后我们可以通过辅助数组得到结果。您可以在下面的代码中看到逻辑:

#include<iostream>
#include<cstring>

using namespace std;

// A utility function to get max of two integers
int max (int x, int y) { return (x > y)? x : y; }

// Returns the length of the longest palindromic subsequence in seq
int lps(char *str,char *result)
{
   int n = strlen(str);
   int i, j, cl;
   int L[n][n];  // Create a table to store results of subproblems

   int Way[n][n];// Store how the palindrome come from.


   // Strings of length 1 are palindrome of lentgh 1
   for (i = 0; i < n; i++)
   {
       L[i][i] = 1;
       Way[i][i]=0;
   }


    // Build the table. Note that the lower diagonal values of table are
    // useless and not filled in the process. The values are filled in a
    // manner similar to Matrix Chain Multiplication DP solution (See
    // http://www.geeksforgeeks.org/archives/15553). cl is length of
    // substring
    for (cl=2; cl<=n; cl++)
    {
        for (i=0; i<n-cl+1; i++)
        {
            j = i+cl-1;
            if (str[i] == str[j] && cl == 2)
            {
                   L[i][j] = 2;
                   Way[i][j]=0;     
            }

            else if (str[i] == str[j])
            {
                  L[i][j] = L[i+1][j-1] + 2;
                  Way[i][j]=0;
            }

            else
            {
                if(L[i][j-1]>L[i+1][j])
                {
                   L[i][j]=L[i][j-1];
                   Way[i][j]=1;                    
                }
                else
                {
                    L[i][j]=L[i+1][j];
                    Way[i][j]=2;  
                }

            }

        }
    }

    int index=0;
    int s=0,e=n-1;

    while(s<=e)
    {
         if(Way[s][e]==0)
         {
             result[index++]=str[s];
             s+=1;
             e-=1;

         }
         else if(Way[s][e]==1)e-=1;
         else if(Way[s][e]==2)s+=1;     
    }

    int endIndex=(L[0][n-1]%2)?index-1:index;

    for(int k=0;k<endIndex;++k)result[L[0][n-1]-1-k]=result[k];

    result[index+endIndex]='\0';


    return L[0][n-1];
}

/* Driver program to test above functions */
int main()
{
    char seq[] = "GEEKSFORGEEKS";
    char result[20];
    cout<<"The lnegth of the LPS is "<<lps(seq,result)<<":"<<endl;
    cout<<result<<endl;
    getchar();
    return 0;
}

希望对你有帮助!

下面是解释:

令 X[0..n-1] 为长度为 n 的输入序列,L(0, n-1) 为 X[0..n-1] 的最长回文子序列的长度。

总共有5个案例。

1)每个字符都是长度为 1 的回文。 对于给定序列中的所有索引 i,L(i, i) = 1。

2) 只有 2 个字符,并且两个字符相同。 L(i, j) = 2。

3)有两个以上字符,首尾字符相同 L(i, j) = L(i + 1, j - 1) + 2

4)首尾字符不相同且L(i + 1, j)

5)首尾字符不相同且L(i + 1, j)>=L(i, j - 1)。 L(i, j) = L(i + 1, j)。

我们可以观察到,只有在情况 1,2 和 3 中,字符 X[i] 才会包含在最终结果中。我们用一个二维辅助数组来表示回文子序列是怎么来的。 案例 1、2、3 的值为 0;案例 4 的值为 1;案例 5 的值为 2。

用辅助数组方式。我们可以得到如下结果:

Let two variables s=0 and e=n-1.
While s<=e
Loop
    If Way[s][e]==0 Then X[s] should be included in the result and we store it in our result array.
    Else if Way[s][e]==1 Then X[s] should not be include in the result and update e=e-1 (because our result comes from case 4).
    Else if Way[s][e]==2 Then X[s] should not be include in the result and update s=s+1 (because our result comes from case 5).

当 s>e 时应该终止循环。这样我们可以得到一半的结果,我们可以很容易地扩展它来得到整个结果。

【讨论】:

  • 你能解释一下逻辑吗?
  • 这在回文长度为奇数时不起作用。假设得到的回文子序列是“acbca”,此时不打印b。
  • 这在回文长度为奇数时不起作用。假设字符串是“acbdca”,得到的回文子序列应该是“acbca”,此时不打印b。
  • @Satish,我已经在我的电脑上试过了,它可以工作。
  • 当输入为“AABCDEBAZ”时,这不起作用。输出为“AABCDCBAA”,长度为 9
【解决方案2】:

在动态编程表中为每个单元格保留一个反向指针和一个值。然后按照从表尾的回溯来重构子序列。

【讨论】:

    【解决方案3】:

    诀窍是这样的:

    请注意,根据您的第二个字符串的定义,两个字符串的 LCS 也是最长的回文数。

    【讨论】:

    • 这并不总是正确的:例如,ABCAB reversed 是 BACBA,而 ACA 是回文,ACB 也是最长的 LCS,而不是回文。
    • Groo:请澄清。 ABCAB 和 BACBA 都不包含 ACA 或 ACB。我的方法将产生任何字母 A、B 或 C 作为解决方案(因为 ABCAB 不包含长度 >= 2 的回文)。
    • Philip:我认为我们讨论的是子序列而不是子字符串。
    • srbh.kmr:啊,明白了!抱歉,我不是以英语为母语的人。
    【解决方案4】:

    以下解决方案非常简单,不需要额外使用任何其他矩阵。 这里我们只是追溯生成最长回文子序列的路径。

    int lps(char *str)
    {
       int n = strlen(str);
       int i, j, cl;
       int L[n][n];  
       for (i = 0; i < n; i++)
          L[i][i] = 1;
        for (cl=2; cl<=n; cl++)
        {
            for (i=0; i<n-cl+1; i++)
            {
                j = i+cl-1;
                if (str[i] == str[j] && cl == 2)
                   L[i][j] = 2;
                else if (str[i] == str[j])
                   L[i][j] = L[i+1][j-1] + 2;
                else
                   L[i][j] = max(L[i][j-1], L[i+1][j]);
            }
        }
        cout<<L[0][n-1]<<endl;
        i = 0,j = n-1;
        vector<char> result;
        while(i<=j)
        {
            if(str[i]==str[j])
            {
                result.push_back(str[i]);
                i++,j--;
            }
            else if(L[i][j-1]>L[i+1][j])
            {
                j--;
            }
            else
            {
                i++;
            }
        }
        if(L[0][n-1]%2==0)
        {
            for(auto i = result.begin();i!=result.end();i++)
                cout<<*i;
            reverse(result.begin(),result.end());
            for(auto i = result.begin();i!=result.end();i++)
                cout<<*i;
        }
        else
        {
            for(auto i = result.begin();i!=result.end();i++)
                cout<<*i;
            reverse(result.begin(),result.end());
            result.erase(result.begin());
            for(auto i = result.begin();i!=result.end();i++)
                cout<<*i;
        }  
    }
    

    【讨论】:

      【解决方案5】:

      一种Java方法。从计算回文子序列长度期间生成的LPS矩阵构建字符串。

      private static void LongestPalindromicSubsequence(char a[])
      {
          int len=a.length;
          int lps[][]=new int[len][len];
          int l=1;
      
          for(int i=0;i<len;i++)
          {
              lps[i][i]=1;        //---------> Length of subsequence of string of length=1 is 1 <------------
          }
      
      
          for(int subsLen=2;subsLen<=len;subsLen++)
          {
              for( int i=0;i<(len-subsLen+1);i++)
              {
                  int j=i+subsLen-1;
      
                  if(a[i]==a[j]&&subsLen==2)
                  {
                      lps[i][j]=2;
      
                  }
                  else
                  {
                      if(a[i]!=a[j])
                      {
                          lps[i][j]=Math.max(lps[i+1][j],lps[i][j-1]);
                      }
                      else
                      {
                          lps[i][j]=2+lps[i+1][j-1];
                      }
                  }
              }
          }
      
         // System.out.println("Length of longest Palindromic subsequence: "+lps[0][len-1]);
      
          printLongestPalindromicsubsequence(a,lps);
      
      }
      
      private static void printLongestPalindromicsubsequence(char[] a, int[][] lps)
      {
          int len=a.length;
          int end=lps[0][len-1];
          char str[]=new char[end+1];
          str[end--]='\0';
          int i=0,j=len-1;
          while(end>=0&&i<=j)
          {
              if(a[i]==a[j])
              {
                  str[end--]=a[i];
                  i++;
                  j--;
              }
              else
              {
                  if(lps[i+1][j]>lps[i][j-1])
                  {
                      i++;
                  }
                  else
                  {
                      j--;
                  }
              }
          }
      
          if(lps[0][len-1]%2!=0)
          {
              i=0;
              int mid=lps[0][len-1]/2;
              j=str.length-2;
              while(j>mid)
              {
                  str[i++]=str[j--];
              }
          }
          else        
          {  
      
              i=0;
              int mid=lps[0][len-1]/2;
              j=str.length-2;
              while(j>=mid)
              {
                  str[i++]=str[j--];
              }
          }
      
          for(i=0;i<str.length;i++)
              System.out.print(str[i]);
      
      }
      

      【讨论】:

        【解决方案6】:

        一个示例 java 实现。随意对您的评论进行残酷对待。

        public class LongestPalindrome {
            public static void main(String... arguments) {
                final String content = "GOBANANAS";
                String palindrome = getLongestPalindrome(content);
                System.out.println(palindrome);
            }
        
            private static String getLongestPalindrome(final String content) {
                String lastPalindrome = "";
                for (int lastIndex = content.length(); lastIndex >= 0; lastIndex--) {
                    for (int i = 0; i <= lastIndex; i++) {
                        String part = content.substring(i, lastIndex);
                        if (part.length() > lastPalindrome.length() && part.length() > 1) {
                            boolean isPalindrome = isPalindrome(part);
                            if (isPalindrome) {
                                lastPalindrome = part;
                                System.out.println(String.format("%s : %s", part, isPalindrome));
                            }
                        }
                    }
                }
        
                return lastPalindrome;
            }
        
            private static boolean isPalindrome(String string) {
                String reverse = (new StringBuilder(string)).reverse().toString();
                return (string.equals(reverse));
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-04-14
          • 2018-05-13
          • 2011-06-15
          • 2014-11-12
          • 2017-02-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多