字符串回文子序列问题

【问题1】求字符串回文子序列的个数

问题:

给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为”a”, “a”, “aa”, “b”, “aba”,共5个。内容相同位置不同的子序列算不同的子序列。

分析与解法:
(注意:与子串不同,子序列可以是不连续的,只要元素的前后相对位置不变。)

解法一:递归
假设s[0 … n-1]是给定的序列,长度为让 c(0,n-1)表示序列s[0 … n-1] 的回文子序列的个数。

  1. 如果s的最后一个元素和第一个元素是相同的:c(1, n-1) + c(0, n-2) + 1。

  2. 如果s的最后一个元素和第一个元素是不同的,这时:c(0, n-1) = c(1, n-1) + c(0, n-2) - c(1, n-2) ,即”ab”+ “ba” - “b” 。

可以写出如下递归程序:

int count(string str, int i, int j)//调用时count(str, 0, n-1);
{
    if(i == j)//一个元素即是它本身
        return 1;
    if(i > j)//只计算顺序时的子序列
        return 0;
    if(str[i] != str[j])//若首尾不同
        return count(str, i + 1, j) + count(str, i, j - 1) - count(str, i + 1, j - 1);
    else//若首尾相同
        return count(str, i + 1, j) + count(str, i, j - 1) + 1;
}

解法二:动态规划
解法一中存在重复计算的问题,且当字符串长度过大时,递归的时间复杂度会很高。可以采用动态规划的方法。
用dp[j][j+i]表示s[j…j+i]之间的回文子序列的个数,

  1. 如果s的最后一个元素和第一个元素是相同的:dp[j][j+i] = dp[j+1][j+i] + dp[j][j+i-1] + 1。
  2. 如果s的最后一个元素和第一个元素是不同的,这时:dp[j][j+i] = dp[j+1][j+i] + dp[j][j+i-1] - dp[j+1][j+i-1],即”ab”+ “ba” - “b”。

这里可以将二维数组dp的值列出,方便理解。其中橙色区域的数值只与黄色区域的数值有关,以dp[0][1]为例,若首尾元素相同,则等于dp[1][1] + dp[0][0] + 1;若首尾元素不同,则等于dp[1][1] + dp[0][0] - dp[1][0]。

这里写图片描述

int count(string str,int n)
{
    int dp[MAXSIZE][MAXSIZE], tmp;
    memset(dp,0,sizeof(dp));//dp数组中所有元素置0
    for(int i=0; i<n; i++) dp[i][i] = 1;//一个元素即是它本身
    //i表示当前长度为i+1的子序列
    for(int i=1; i<n; i++)
    {
        tmp = 0;
        //考虑所有连续的长度为i+1的子串. 该串为 str[j, j+i]
        for(int j=0; j+i<n; j++)
        {
            if(str[j] == str[j+i])//若首尾相同
                tmp = dp[j+1][j+i] + dp[j][j+i-1] + 1;
            else//若首尾不同
                tmp = dp[j+1][j+i] + dp[j][j+i-1] - dp[j+1][j+i-1];
            dp[j][j+i] = tmp;
        }
    }
    //返回串 str[0][n-1] 的结果
    return dp[0][n-1];
}

【问题2】求字符串回文子序列的最大长度

http://www.acmerblog.com/longest-palindromic-subsequence-5721.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值