UVa 10617 Again Palindrome(回文 区间dp)

A palindorme is a sequence of one or more characters that reads the same from the left as it does from the right. For example, Z, TOT and MADAM are palindromes, but ADAM is not.
Given a sequence S of N capital latin letters. How many ways can one score out a few symbols (maybe 0) that the rest of sequence become a palidrome. Varints that are only different by an order of scoring out should be considered the same.
Input
The input file contains several test cases (less than 15). The first line contains an integer T that indicates how many test cases are to follow.
Each of the T lines contains a sequence S (1 ≤ N ≤ 60). So actually each of these lines is a test case.
Output
For each test case output in a single line an integer — the number of ways.
Sample Input
3
BAOBAB
AAAA
ABA

Sample Output
22 15 5

题目大意:
给一个长度《=60的字符串,删去其中一些字符,使之成为回文串。问一共有多少种删法。形如 T ,TOT都是回文串。
思路:
最 朴素的想法:是 先枚举要删的个数x,再枚举出删除x个字符的所有情况,一一判断。显然这样复杂度很高,也不好操作。

于是我们先考虑最简单的情况:
1.只有两个字符。如果这两个字符相等答案就是各自一种情况+组合在一起的一种情况=2+1=3;如果不相等 就只是各自的情况相加=2。
2.再考虑三个字符。形如:ABA 边缘的两个字符相等。 我们可以看成是AB+A ,A+AB,A+B+A。可以发现 当前的要求的结果 是可以利用一个更小的子问题的结果来求解的。
3.多个字符。A+ABCBA+A。
要解决当前的问题,需要利用更小的一个子问题,,,一直分解下去,就会变成最简单的只含一个字符 的问题。于是发现这是一个动归。满足最优子结构。
而影响决策的因素 只有边缘两个字符是否相等。
想到是dp很容易,,但动归方程写的时候要想清楚。
1.s[i]==s[j],此时dp[i][j]=dp[i+1][j] (left)+dp[i][j-1] (right)-dp[i+1][j-1] (中间重复计算部分)+dp[i+1][j-1] (边缘字符都留下会增加的部分)+1(只留下边缘的两个字符);
2.s[i]!=s[j],这时边缘两个字符肯定是无法同时存在的了。所以就只有上一个方程的前一部分dp[i][j]=dp[i+1][j] (left)+dp[i][j-1] (right)-dp[i+1][j-1] (中间重复计算部分);
边缘条件是dp[i][i]=1;答案在dp[0][n-1] 里。

感受:
1.对于不难想到的dp问题 一定要注意转移方程考虑的全面性。。。。
2.数组一定要开longlong。。。

//  Created by ZYD in 2015.
//  Copyright (c) 2015 ZYD. All rights reserved.
//

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define maxn 1000000
#define ll long long
#define mk make_pair
#define pb push_back
#define mem(array) memset(array,0,sizeof(array))
const double EPS=1e-9;
typedef pair<int,int> P;
ll T,dp[100][100];
char s[65];
int main()
{
    freopen("in.txt","r",stdin);
    cin>>T;
    while(T--)
    {
        cin>>s;
        mem(dp);
        int n=strlen(s);
        for(int i=0;i<n;i++) dp[i][i]=1;
        for(int j=1;j<n;j++)
            for(int i=j-1;i>=0;i--)
                if(s[i]==s[j]) dp[i][j]=dp[i+1][j]+dp[i][j-1]+1;
                else dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1];       

        cout<<dp[0][n-1]<<endl; 

    }

    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值