如何找到最长的回文子序列(不是它的长度)

时间:2012-10-15 09:37:06

标签: algorithm dynamic-programming

我想找出一个字符串中最长的回文子序列。在任何地方我都找到算法来找出子序列的长度,并且声明可以扩展算法以返回子序列,但是我找不到如何。任何人都可以解释我怎样才能得到序列?

6 个答案:

答案 0 :(得分:8)

由于你在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)&lt; L(i,j - 1)。 L(i,j)= L(i,j - 1)。

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。

用辅助阵列Way。我们可以得到如下结果:

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时,应该终止循环。通过这种方式,我们可以得到结果的一半,我们可以轻松扩展它以获得整个结果。

答案 1 :(得分:4)

为每个单元格保留动态编程表中的backpointer和值。然后按照表末尾的追溯来重建子序列。

答案 2 :(得分:1)

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

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;
    }  
}

答案 3 :(得分:0)

诀窍就是这样:

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

答案 4 :(得分:0)

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]);

}

答案 5 :(得分:0)

一个示例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));
    }
}