GEEK编程练习— —最长回文子序列LPS

最长回文子序列LPS(Longest Palindromic Subsequence)也是编程题目里面经常出现的一种,但是更经常出现的是最长回文子串(Longest Palindromic Substring),英文简写都是LPS,所以在搜索的时候不要搞混了。
主要区别
子串是连续的,字符之间不能有间隔;而子序列可以是不连续的。如字符串cabbeaf,它的最长回文子串是bb,而最长回文子序列是abba。

题目

输入一个字符串,然后输出最长回文子序列的长度。

分析

假设S[0…n-1]是给定的字符串序列,长度为n,用lps(0,n-1)表示序列S[0…n-1]的最长回文子序列的长度。
1)如果S长度为1,那么最长回文自序列就是1;

2)如果S最后一个字符和第一个字符相同,则lps(0,n-1)=lps(1,n-2)+2;以字符串“1324332431”为例,第一个字符与最后一个字符相同,lps(1,n-2)表示红色部分的最长回文子序列的长度;

3)如果S的最后一个字符与第一个字符不同,则lps(0,n-1)=max(lps(1,n-1),lps(0,n-2));以字符串“32433243”为例,lps(1,n-1)表示去掉第一字符的子序列,lps(0,n-2)表示去掉最后一个字符的子序列。

代码

递归

时间复杂度为O(2^n) ,空间复杂度O(n)。

//LPS(Longest Palindromic Subsequence)
//Date:2016-04-04
//Author:Sin_Geek

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int lps(const string &str, int begin, int end)
{
    //begin大于end时返回0
    if (begin > end)
        return 0;

    //只有一个字符,回文长度为1
    if (begin == end)
        return 1;

    //首尾相同
    if (str[begin] == str[end])
        return lps(str, begin + 1, end - 1) + 2;

    //首尾不同
    return max(lps(str, begin, end - 1), lps(str, begin + 1, end));
}

int main()
{
    string str;
    cin >> str;
    cout << lps(str, 0, str.size() - 1) << endl;

    return 0;
}

自顶向下的动态规划

时间复杂度为O(n^2) ,空间复杂度O(n^2)。

//LPS(Longest Palindromic Subsequence)
//Date:2016-04-04
//Author:Sin_Geek


#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int lps(const string &str, int begin, int end, vector<vector<int>> &f)
{
    //begin大于end时返回0
    if (begin > end)
        return 0;

    //只有一个字符,回文长度为1
    if (begin == end)
        return f[begin][end] = 1;

    //如果子串str[begin...end]已经计算过,避免计算直接返回
    if (f[begin][end])
        return f[begin][end];

    //首尾相同
    if (str[begin] == str[end])
        return f[begin][end] = lps(str, begin + 1, end - 1, f) + 2;

    //首尾不同
    return f[begin][end] = max(lps(str, begin, end - 1, f), lps(str, begin + 1, end, f));

}

int main()
{
    string str;
    cin >> str;
    int n = str.size();
    vector<vector<int>> f(n, vector<int>(n, 0));
    cout << lps(str, 0, n-1, f) << endl;

    return 0;
}

自底向上的动态规划

时间复杂度为O(n^2) ,空间复杂度O(n^2)。

//LPS(Longest Palindromic Subsequence)
//Date:2016-04-04
//Author:Sin_Geek

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    string str;
    cin >> str;
    int n = str.size();

    //创建二维数组f[i][j]表示str[i..j]的最长回文子序列。
    vector<vector<int>> f(n,vector<int>(n));

    for (int i = n - 1; i >= 0; --i)
    {
        f[i][i] = 1;
        for (int j = i + 1; j < n; ++j)
            if (str[i] == str[j])
                f[i][j] = f[i + 1][j - 1] + 2;
            else
                f[i][j] = max(f[i][j - 1], f[i + 1][j]);
    }
    cout << f[0][n - 1] << endl;

    return 0;
}

自底向上的动态规划改进版

上面的那个自顶向上动态规划版本,空间复杂度为O(n^2),其实计算第i行时只用到了第i+1行,跟其他的无关,所以只需要2行即可。

可以先在第0行计算f[n-1],然后用第0行的结果在第1行计算f[n-2],再用第1行的结果在第0行计算f[n-3],以此类推。正在计算的那行设为now,那么计算第now行时,就要用第1-now行的结果。当计算完成时,如果s.length()是奇数,则结果在第0行;如果是偶数,则结果在第1行。

时间复杂度为O(n^2) ,空间复杂度O(n)。

//LPS(Longest Palindromic Subsequence)
//Date:2016-04-04
//Author:Sin_Geek

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    string str;
    cin >> str;
    int n = str.size(), now = 0;


    //创建二维数组f[i][j]表示str[i..j]的最长回文子序列。
    vector<vector<int>> f(2, vector<int>(n));

    for (int i = n - 1; i >= 0; --i)
    {
        f[now][i] = 1;
        for (int j = i + 1; j < n; ++j)
            if (str[i] == str[j])
                f[now][j] = f[1 - now][j - 1] + 2;
            else
                f[now][j] = max(f[now][j - 1], f[1 - now][j]);
        now = 1 - now;
    }

    if (n % 2 == 0)
        cout << f[1][n - 1] << endl;
    else
        cout << f[0][n - 1] << endl;

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值