动态规划算法之不同回文子串个数(字符内容相同,位置不同算一个子串)

Problem Description
Given a string S, find the number of different non-empty palindromic subsequences in S, and return that number modulo 10^9 + 7.

A subsequence of a string S is obtained by deleting 0 or more characters from S.

A sequence is palindromic if it is equal to the sequence reversed.

Two sequences A_1, A_2, … and B_1, B_2, … are different if there is some i for which A_i != B_i.

Example 1:

Input:
S = ‘bccb’
Output: 6
Explanation:
The 6 different non-empty palindromic subsequences are ‘b’, ‘c’, ‘bb’, ‘cc’, ‘bcb’, ‘bccb’.
Note that ‘bcb’ is counted only once, even though it occurs twice.

Example 2:

Input:
S = ‘abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba’
Output: 104860361
Explanation:
There are 3104860382 different non-empty palindromic subsequences, which is 104860361 modulo 10^9 + 7.

Note:
The length of S will be in the range [1, 1000].
Each character S[i] will be in the set {‘a’, ‘b’, ‘c’, ‘d’}

建立递推关系
用dp[i][j]表示字符串中下标从i到j的范围内不同回文子串个数,首先分析如下例子,以此来推导递推关系式

在字符串S中,若S[i]!=S[j],dp[i][j]=dp[i][j-1]+dp[i+1][j]-dp[i+1][j-1]
若S[i]==S[j],则分以下三种情况:
若S[i+1]~S[j-1]之间没有字符等于S[i],则dp[i][j]=dp[i+1][j-1]*2+2
若S[i+1]~S[j-1]之间有一个字符等于S[i],则dp[i][j]=dp[i+1][j-1]*2+1
若S[i+1]~S[j-1]之间至少有两个字符等于S[i],则dp[i][j]=dp[i+1][j-1]*2-dp[ii+1][jj-1],其中ii为第一个等于S[i]的字符的下标,jj为最后一个等于S[i]的字符的下标。

代码如下

#include <iostream>
#include <string>
#define MAXN 1000
using namespace std;
int dp[MAXN][MAXN];
long int M=1E9+7;
int countPalindromicSubsequences(string S) {
        int n=S.length();
        for(int i=0;i<n;i++)
            dp[i][i]=1;                 //dp[i][j]表示字符串中下标从i到j的不同回文子串个数
       for(int len=1;len<n;len++)
            for(int i=0;i<n-len;i++)
            {
                int j=i+len;
                if(S[i]==S[j])
                {
                    int ii=i+1;
                    while(S[ii]!=S[i])
                        ii++;
                    int jj=j-1;
                    while(S[jj]!=S[j])
                        jj--;
                    if(ii==jj)
                        dp[i][j]=dp[i+1][j-1]*2+1;  //若s[i+1]到s[j-1]之间只有一个s[i]
                    else if(ii>jj)
                        dp[i][j]=dp[i+1][j-1]*2+2;  //若s[i+1]到s[j-1]之间没有s[i]
                    else
                        dp[i][j]=dp[i+1][j-1]*2-dp[ii+1][jj-1]; //若s[i+1]到s[j-1]之间至少有两个s[i],其中ii为第一个s[i]的下标,jj为最后一个s[i]的下标
                }  //if
                else
                    dp[i][j]=dp[i][j-1]+dp[i+1][j]-dp[i+1][j-1];
                dp[i][j]=(dp[i][j]+M)%M;  //在取余数情况下,要避免减法运算结果出现负数
            }  //for
               return dp[0][n-1]; 
        }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值