#1149 : 回文字符序列

1149 : 回文字符序列

时间限制:2000ms
单点时限:1000ms
内存限制:256MB
描述
给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为”a”, “a”, “aa”, “b”, “aba”,共5个。内容相同位置不同的子序列算不同的子序列。

输入
第一行一个整数T,表示数据组数。之后是T组数据,每组数据为一行字符串。

输出
对于每组数据输出一行,格式为”Case #X: Y”,X代表数据编号(从1开始),Y为答案。答案对100007取模。

数据范围
1 ≤ T ≤ 30

小数据

字符串长度 ≤ 25

大数据

字符串长度 ≤ 1000

样例输入
5
aba
abcbaddabcba
12111112351121
ccccccc
fdadfa
样例输出
Case #1: 5
Case #2: 277
Case #3: 1333
Case #4: 127
Case #5: 17

http://hihocoder.com/problemset/problem/1149

区间dp问题 = =
dp[i][j] 代表i到j区间内的回文子串数量。
那么它的来源有两个。一个是 dp[i+1][j] 和 dp[i][j-1];
这里注意的地方是 , dp[i+1][j] 和 dp[i][j-1]都重复的包含了dp[i+1][j-1];这个区间的字串。所以要减去一个。
最后注意的地方是 如果 s[i]==s[j]的时候。
那么dp[i][j]要再加上dp[i+1][j-1]+1;
因为这个时候。i和j 这两个地方的字符。在和区间i+1到j-1的回文字串中,首先获得这么多组合,之后又新增了一个组合。
因为dp[i+1][j] 和 dp[i][j-1]并没有包含dp[i+1][j-1]和s[i],s[j],组合的新回文串。

因为懒得想循环,直接递归操作了- -;
因为发现写带返回值的递归- - 需要记忆化。和用一个dp记录没什么区别,直接写dp了。

我们如果想知道1, 6区间的回文字串,那么首先要知道 i+1,j和 i,j-1 这两个区间的东西。
也就是 2 6 和1 5;
这个时候 继续递归下去,
如果想知道2 6 要先知道 3 6 和 2 5 ;
然后继续下去 。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
using namespace std;
string a;
const int mod=1e5+7;
int dp[1005][1005];
void dfs(int l,int r)
{
    if(l>r) return ;
    if(l==r)
    {
        dp[l][r]=1;
        return ;
    }
    if(!dp[l+1][r]) dfs(l+1,r);//这两步属于记忆化,不算重复的- -。
    if(!dp[l][r-1]) dfs(l,r-1);
    dp[l][r]=(dp[l+1][r]+dp[l][r-1]-dp[l+1][r-1]+mod)%mod;
    if(a[l]==a[r]) dp[l][r]=(dp[l][r]+dp[l+1][r-1]+1)%mod;
}
int main()
{
    int t;
    cin>>t;
    int w=1;
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        cin>>a;
        int l=strlen(&a[0]);
        dfs(0,l-1);
        cout<<"Case #"<<w++<<": "<<dp[0][l-1]<<endl;
    }
}

然后想着写循环- -。
至于循环方面。

        for(int j=0;a[j];j++)
        {
            dp[j][j]=1;
            for(int i=j-1;i>=0;i--)//关于为什么倒着。因为这样合理的遍历了所有东西。
            {
                dp[i][j]=(dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+mod)%mod;
                if(a[i]==a[j])
                {
                    dp[i][j]=(dp[i][j]+dp[i+1][j-1]+1)%mod;
                }
            }
        }

写dp的循环也很重要。
要确定循环的方式。这时候需要一个完美的逻辑。完美的逻辑不是猜出来的。
对于这个题 是递归的思考方式,推理出来的。

#include <bits/stdc++.h>
using namespace std;
int dp[1005][1005];
const int mod=1e5+7;
int main()
{
    int t;
    cin>>t;
    int w=1;
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        string a;
        cin>>a;
        int l=strlen(&a[0]);
        for(int j=0;a[j];j++)
        {
            dp[j][j]=1;
            for(int i=j-1;i>=0;i--)//关于为什么倒着。因为这样合理的遍历了所有东西。
            {
                dp[i][j]=(dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+mod)%mod;
                if(a[i]==a[j])
                {
                    dp[i][j]=(dp[i][j]+dp[i+1][j-1]+1)%mod;
                }
            }
        }
        cout<<"Case #"<<w++<<": "<<dp[0][l-1]<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值