动态规划(二)最长回文子序列

1.题目描述:

给一个字符串,找出它的最长的回文子序列的长度。和腾讯的2016实习题目类似。
输入:goolgle
输出:4
goog是它的最长回文子序列

所说的子序列不一定是连续的,但是顺序不变。
子串必须是连续的字符组成。

2.分析

最优子结构

假设S[i…j]是给定的字符串,长度为n,让dp[i][j]表示从s[i]到s[j]包含的最长回文子序列的长度。
初始化:dp[i][i] = 1;
如果s[i] =s[j],dp[i][j] = dp[i+1][j-1] +2;
如果s[i] != s[j],dp[i][j] = max(dp[i][j-1],dp[i+1][j])
可以得到它的递归程序

int lps(char *s ,int i,int j)
{
    if(i == j)
        return 1;
    if(i>j)
        return 0;
    if(s[i] == s[j])
        return lps(s,i+1,j-1)+2;
    else
        return max(lps(s,i,j-1),lps(s,i+1,j));

}
重叠子问题

以一个长度为6的字符串为例:
动态规划(二)最长回文子序列_第1张图片
L(1,4)出现重复计算,符合动态规划的性质。

动态规划的方法

采用自底向上的方法,将子序列的长度作为排序的依据,按从小到大进行求解。

#include 
#include 
#include 
using namespace std;
int Max(int i,int j)
{
    if(i > j)
        return i;
    else
        return j;
}
int longestpalind(string s) {
    int n = s.size();
    int dp[n][n];
//  int(*dp)[n];     动态分配数组空间
//  dp = (int (*)[n])malloc(n*n*sizeof(int));
    int temp;
    int i,j,result;
    for(i = 0;i< n;i++)
        dp[i][i] = 1;
    for(i = 1;i< n;i++)  //子序列长度从小到大
    {
        temp = 0;
        for(j = 0;j+i < n;j++)
        {
            if(s[j] == s[j+i])
                temp = dp[j+1][j+i-1]+2;
            else {
                temp = Max(dp[j][j+i-1],dp[j+1][j+i]);
            }
            dp[j][j+i] = temp;
        }
    }
    result = dp[0][n-1];
    //free(dp);
    return result;

}
int main()
{
    string s = "google";
    cout << longestpalind(s) << endl;
    return 0;
}

在定义数组时需要考虑一个问题,数组的维数为变量,使用变长数组(VLA)或者是动态分配数组内存(malloc),在C99中加入了对变长数组的支持,在linux下的g++ 编译器是支持的,但是在有的编译器下是非法的,所以最好是通过动态分配内存的方法来定义数组。
变长数组的特点就是:自动存储,所用的内存空间再运行完定义部分之后会自动释放。对多维数组来说更方便。注意使用变长数组的定义时,如果出现数组访问越界,是不会报错的。
如果使用new来分配内存。

int **sp = new int *[n];
for(i = 0;inew int[n];
//释放时
for(i = 0;idelete [] sp[i];
delete []sp;

最长回文子串

注意这里子串是连续的,仍然使用动态规划的方法。
1.dp[i][j]表示子串s[i…j]是否为回文
2.初始化:dp[i][j] = true (0,n-1);dp[i][i-1] = true(k= 2时使用)。
3.dp[i][j] =(s[i] == s[j] && dp[i+1][j-1] == true)

#include 
#include 
using namespace std;

int lps(string s)
{
    int n = s.size();
    bool dp[n][n];
    int resleft= 0 ,resright =0;
    int i,j;
    dp[0][0] = true;
    for(i = 1;i< n;i++)
    {
        dp[i][i] = true;
        dp[i][i-1] = true;  //k=2时使用
    }
    for(i = 2;i<=n;i++)
    {
        for(j = 0;j <= n-i;j++)
        {
            if(s[j] == s[j+i-1] && dp[j+1][j+i-2])
            {
                dp[j][j+i-1] = true;
                if(resright -resleft +1 1;
                }
            }
        }
    }
    int result = resright-resleft+1;
    return result;
}
int main()
{
    string s = "google";
    cout << lps(s) << endl;
    return 0;
}

方法二:编译回文子串的中心元素,分别计算偶数长度和奇数长度的回文子串。

#include 
#include 

using namespace std;

int lps(string s)
{
    int n = s.size();
    int maxlen = 0;
    int i,j,start;
    for(i = 1;i< n;i++)
    {
        int low = i-1; //以i-1,i为中心的回文
        int high = i;
        while(low >=0 && high < n&& s[low] == s[high])
        {
            low--;
            high++;
        }
        if(high-low-1 > maxlen) //high-1-(low +1)+1 = len= high-low-1
        {
            maxlen = high-low-1;
            start = low+1;
        }
        low = i-1;  //以i为中心的回文
        high = i+1;
        while(low >=0 && high < n && s[low] == s[high])
        {
            low--;
            high++;
        }
        if(high-low-1 > maxlen)
        {
            maxlen = high-low-1;
            start = low+1;
        }
    }
    return maxlen;
}
int main()
{
    string s = "google";
    cout << lps(s) << endl;
    return 0;
}

你可能感兴趣的:(算法学习,动态规划,回文子序列)