UVa 10617 Again Palindrome(回文串区间DP)

UVa 10617 Again Palindrome(经典回文串区间DP)

题意:

给定一个字符串s,对s进行删除操作,使得剩下的子串是回文字符串,问最多有多少种这种子串。

思路:

涉及到回文字符串,首先要想到的肯定是区间DP,如何写出状态转移方程?

直接从题意切入:dp[i, j]表示区间[i, j]最多有多少个这样的子串。

1. s[i] == s[j] 去掉s[i],则一个子问题就是dp[i+1, j]; 去掉s[j],另一个子问题就是dp[i, j-1];

显然这两个子问题是会有重叠的,重叠的部分就是dp[i+1, j-1],所以要减去这部分。

如果s[i], s[j]都不去掉呢?因为s[i] == s[j], s[i]和s[j]组成一个子回文字串,并且s[i],s[j]和dp[i+1, j-1]

里面包含的所有子回文字串都构成回文字串,则这个子问题的解显然是dp[i+1, j-1] + 1。

于是会有 dp[i][j] = dp[i+1][j] + dp[i][j-1] + dp[i+1, j-1] + 1 - dp[i+1, j-1];

化简为 dp[i][j] = dp[i+1][j] + dp[i][j-1] + 1;

2. s[i] != s[j] 此时的子问题就比上述要简单了,因为s[i] s[j]与dp[i+1, j-1]的回文子串构成不了回文串。

于是 dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1];

<span style="font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;

#define ll long long
const double PI = acos(-1.0);
const double e = 2.718281828459;
const double eps = 1e-8;
const int MAXN = 65;
ll dp[MAXN][MAXN];
char s[MAXN];

int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int Case;
    cin>>Case;
    while(Case--)
    {
        scanf("%s", s+1);
        int len = strlen(s+1);
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= len; i++)
        {
            dp[i][i] = 1;
        }
        for(int k = 2; k <= len; k++)
        {
            for(int i = 1, j = k; j <= len; i++, j++)
            {
                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[1][len]<<endl;
    }
    return 0;
}</span>

下面这种为递归写法,记忆化搜索。

<span style="font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;

#define ll long long
#define max(a, b) (a)>(b)?(a):(b)
const double PI = acos(-1.0);
const double e = 2.718281828459;
const double eps = 1e-8;
const int MAXN = 65;
ll dp[MAXN][MAXN];
char s[MAXN];

ll dfs(int l, int r)
{
    if(dp[l][r] != -1)
        return dp[l][r];
    if(l >= r)
        return dp[l][r] = (l==r)?1:0;
    if(s[l] == s[r])
        dp[l][r] = max(dp[l][r], dfs(l+1, r)+dfs(l, r-1)+1);
    else
        dp[l][r] = max(dp[l][r], dfs(l+1, r)+dfs(l, r-1)-dfs(l+1, r-1));
    return dp[l][r];
}

int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int Case;
    cin>>Case;
    while(Case--)
    {
        scanf("%s", s+1);
        int len = strlen(s+1);
        memset(dp, -1, sizeof(dp));
        dp[1][len] = dfs(1, len);
        cout<<dp[1][len]<<endl;
    }
    return 0;
}</span>
时间: 2024-08-02 14:33:47

UVa 10617 Again Palindrome(回文串区间DP)的相关文章

回文串区间dp

UVa 10739 String to Palindrome(经典回文串区间DP) 题意: 给定一个字符串,可以对其进行删除,插入,替换操作. 问最少经过几次操作,可以使这个字符串变成回文字符串. 思路: 看得别人的 题解,最优化问题,用较为直接的方法处理时发现情况很复杂,很多时候就要考虑动态规划了.先从整体出发,由大到小,看往少一个两个元素的情况进行最优递归,如何得到结果. (这里区间DP即是不断向两侧扩大规模) (1)如果最外层两个字符相同,s[0]==s[n],那么这两个字符外侧肯定不用考

poj 1159 Palindrome -- 回文串,动态规划

Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 59029   Accepted: 20505 Description A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a

HDU 1513[Palindrome] 回文串

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1513 题目大意:给一个字符串,问最少加多少个字母能成为回文串. 关键思想:要解决的是回文子序列问题而不是回文子串.回文子序列怎么求?可以把字符串倒转一下,再求他们的最长公共子序列啊!想一想为什么.求出LCS长度之后,n-LCS才是我们要的答案,因为对于不是回文子序列的其他字符来说,都需要给他们一个对象才能回文(对称).还有一个问题是我们开不了5000*5000的数组,但观察到递推方程是只与两个状态有

UVa 11584 划分成回文串

https://vjudge.net/problem/UVA-11584 题意: 给出一串字符,把它划分成尽量少的回文串. 思路: 用d[i]表示划分到i时所能划分的最小个数,转移方程为d[i]=min{d[i],d[j]+1},当然前提是j+1~i是回文串,我们可以预处理计算出所有的回文串,这样转移时就比较方便. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<s

回文串 --- 动态dp UVA 11584

题目链接: https://cn.vjudge.net/problem/34398/origin 本题的大意其实很简单,就是找回文串,大致的思路如下: 1. 确定一个回文串,这里用到了自定义的check函数原理如下: 传入le, ri两个值(定义从1开始), s+1 = aaadbccb. a a a d b c c b 1 2 3 4 5 6 7 8 比如,le = 5, ri = 8. 则s[5] == s[8]成立 le++ ri-- 再比较 s[6] == s[7]? 成立 le++,

关于回文串的DP问题

问题1:插入/删除字符使得原字符串变成一个回文串且代价最小 poj 3280 Cheapest Palindrome 题意:给出一个由m中字母组成的长度为n的串,给出m种字母添加和删除花费的代价,求让给出的串变成回文串的代价. Sol: 插入和删除等价,因此只需要保留 min(插入代价,删除代价)作为调整字符串的代价 如果 s[i]==s[j],那么将区间(i,j)变为回文串的代价和将区间(i+1,j-1)变为回文串的代价相同,因为此时不需要修改 如果不同,必然要将 s[i]和s[j]改为同一字

最长回文子序列 区间dp

J - 买票回家啦 Time Limit:1000MS    Memory Limit:65535KB    64bit IO Format: SubmitStatusPracticeNBUT 1586 Description 集训要结束了,同学们就准备回家了.好舍不得回家阿.(那就再待一个月嘛,就这么愉快地决定了.)超哥要回家了,可是他没有挤进12306官网, 可怜的他就随便找了个能代购车票的网站.结果,当他付钱时傻眼了,这个网站竟然要验证码.验证码嘛就照着样子输入就好了呀,哦不,这个网站管理

bzoj 1710: [Usaco2007 Open]Cheappal 廉价回文【区间dp】

只要发现添加一个字符和删除一个字符是等价的,就是挺裸的区间dp了 因为在当前位置加上一个字符x就相当于在他的对称位置删掉字符x,所以只要考虑删除即可,删除费用是添加和删除取min 设f[i][j]为从i到j的价格,长度从小到大枚举更新就行了 f[i][j]=min(f[i][j-1]+cost[s[j]],f[i+1][j]+cost[s[i]]),如果s[i]==s[j]还能和f[i+1][j-1]取个min cpp #include<iostream> #include<cstdio

Uva 10617 Again Palindrome (DP+回文串)

Problem I Again Palindromes Input: Standard Input Output: Standard Output Time Limit: 2 Seconds 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 palind