关于回文串的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]改为同一字符
  • 第一种情况是,想要将(i,j)变为回文串,可以是在(i+1,j)已是回文串的基础上,在j后面添加字符 s[i],或者直接将i处的字符 s[i] 删掉,取代价小的操作即可
  • 另一种情况是,如果(i,j-1)是回文串,可以将j处的字符删掉或在i前面填加字符s[j],同样取代价小的方式操作

Code:提供几种不同的写法,加深理解

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define mem(a) memset(a,0,sizeof(a))
 4 #define MIN(a,b) ((a) < (b) ? (a) : (b))
 5
 6 int DP[2005][2005],cost[30],N,M;
 7 char str[2005];
 8
 9 int main()
10 {
11     while(~scanf("%d%d", &M, &N))
12     {
13         mem(DP); mem(str); mem(cost);
14         scanf("%s%*c",str);
15         char ch; int x, y;
16         for(int i=0;i<M;i++)
17         {
18             scanf("%c %d %d%*c", &ch, &x, &y);
19             cost[ch-‘a‘] = MIN(x,y);
20         }
21         for(int i=1;i<N;i++)
22         {
23             for(int j=i-1;j>=0;j--)
24             {
25                 DP[j][i] = MIN(DP[j+1][i]+cost[str[j]-‘a‘], DP[j][i-1]+cost[str[i]-‘a‘]);
26                 if(str[i] == str[j])DP[j][i] = MIN(DP[j][i],DP[j+1][i-1]);
27             }
28         }
29         printf("%d\n", DP[0][N-1]);
30     }
31     return 0;
32 }

1

 1     #include<iostream>
 2     #include<algorithm>
 3     using namespace std;
 4     typedef pair<int, int> P;
 5     #define ad first
 6     #define de second
 7     int n, m, dp[2005][2005];
 8     char s[2005];
 9     P ch[28];           //ch[ch-‘a‘].ad代表add一个ch的代价,ch[ch-‘a‘].de代表delete一个ch的代价
10     int main()
11     {
12         cin >> n >> m >> s;
13         for (int i = 1; i <= n; i++)
14         {
15             char Ch;
16             cin >> Ch;
17             cin >> ch[Ch - ‘a‘].ad >> ch[Ch - ‘a‘].de;
18         }
19         for (int i = 1; i <= m; i++)
20             dp[i][i] = 0;
21         for (int i = 1; i < m; i++)             //注意for循环要实现从短串到长串的过渡,这里i代表长度为i+1
22             for (int j = 0; j + i < m; j++)
23                 if (s[j] == s[j + i]) dp[j][j + i] = dp[j + 1][j + i - 1];
24                 else
25                 {
26                     int aa = dp[j + 1][j + i] + min(ch[s[j] - ‘a‘].ad, ch[s[j] - ‘a‘].de);
27                     int bb = dp[j][j + i - 1] + min(ch[s[j + i] - ‘a‘].ad, ch[s[j + i] - ‘a‘].de);
28                     dp[j][j + i] = min(aa, bb);
29                 }
30         printf("%d\n", dp[0][m - 1]);
31         return 0;
32     }  

2

 1     #include <cstdio>
 2     #include <string>
 3     #include <iostream>
 4     #include <algorithm>
 5     #include <cstring>
 6     using namespace std;
 7     const int N = 200;
 8     const int M = 2500;
 9     int add[N];
10     int dp[M][M];
11
12     int main()
13     {
14         int n,m;
15         string s;
16         while(~scanf("%d%d",&n,&m))
17         {
18             cin>>s;
19             char c;int x,y;
20             for(int i=0;i<n;i++)
21             {
22                 cin>>c>>x>>y;
23                 add[c]=min(x,y);
24             }
25             memset(dp,0,sizeof(dp));
26             for(int k=1;k<s.size();k++)
27             {
28                 for(int i=0,j=k;j<s.size();i++,j++)
29                 {
30                     dp[i][j]=0x3f3f3f3f;
31                     if(s[i]==s[j])
32                         dp[i][j]=dp[i+1][j-1];
33                     else
34                     {
35                         dp[i][j]=min(dp[i+1][j] + add[s[i]],dp[i][j]);
36                         dp[i][j]=min(dp[i][j-1] + add[s[j]],dp[i][j]);
37                     }
38                 }
39             }
40             printf("%d\n",dp[0][s.size()-1]);
41         }
42         return 0;
43     }  

3


问题2:插入最少多少个字符使得原字符串变成一个回文串

poj 1159 Palindrome

Sol:

首先第一种方法是:

这道题相当于是上一个题中的修改代价为1的情况

因此列出方程:

从上面的分析可以看出,这个问题的实质是求最长公共子序列,只是这两个序列分别是串S的前一部分和串S后一部分的逆序列。

由此引出第二种方法

第二种方法:

先说结论:设原序列S的逆序列为S‘,最少需要补充的字母数 = 原序列S的长度-S和S‘的最长公共子串长度

最后这道题需要对内存进行优化

Code:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4
 5 using namespace std;
 6
 7 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 8
 9 #define MAXSIZE 5005
10
11 //开始没有考虑内存问题,使用了int型,超内存限制,也可使用滚动数组解决
12 unsigned short d[MAXSIZE][MAXSIZE];
13
14 int ToPalindrome(char *s, int n)
15 {
16     int i, j, k;
17     //只有一个字符时,不需要添加字符
18     for (i = 0; i < n; i++)
19     {
20         d[i][i] = 0;
21     }
22     //串长度为2时
23     for (i = 1; i < n; i++)
24     {
25         if (s[i-1] == s[i])
26         {
27             d[i-1][i] = 0;
28         }
29         else
30         {
31             d[i-1][i] = 1;
32         }
33     }
34
35     //串长度递增
36     for (k = 2; k < n; k++)
37     {
38         for (i = 0, j = k; j < n; i++, j++)
39         {
40             if (s[i] == s[j])
41             {
42                 d[i][j] = d[i+1][j-1];
43             }
44             else
45             {
46                 d[i][j] = MIN(d[i][j-1], d[i+1][j]) + 1;
47             }
48         }
49     }
50     return d[0][n-1];
51 }
52
53 int main(void)
54 {
55     char str[MAXSIZE];
56
57     int n;
58     while (scanf("%d", &n) != EOF)
59     {
60         getchar();
61         gets(str);
62         printf("%d\n", ToPalindrome(str, n));
63     }
64     return 0;
65 }

1

 1 #include<cstring>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6
 7 const int N=5010;
 8 int n;
 9 char a[N],b[N];
10 int f[2][N];
11
12 int main(){
13     while(scanf("%d",&n)!=EOF){
14         scanf("%s",a+1);
15         for(int i=1;i<=n;++i)
16             b[i]=a[n-i+1];
17         memset(f,0,sizeof(f));
18         for(int i=1;i<=n;++i)
19             for(int j=1;j<=n;++j)
20                 if(a[i]==b[j]) f[i%2][j]=f[(i-1)%2][j-1]+1;
21                 else f[i%2][j]=max(f[(i-1)%2][j],f[i%2][j-1]);
22         printf("%d\n",n-f[n%2][n]);
23     }
24     return 0;
25 }

2

时间: 2024-10-23 22:25:40

关于回文串的DP问题的相关文章

回文串区间dp

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

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 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++,

NYOJ 1023 还是回文(DP,花最少费用形成回文串)

1 /* 2 题意:给出一串字符(全部是小写字母),添加或删除一个字符,都会产生一定的花费. 3 那么,将字符串变成回文串的最小花费是多少呢? 4 5 思路:如果一个字符串增加一个字符 x可以形成一个回文串,那么从这个字符串中删除这个字符 x 6 同样也能形成回文串! 7 所以我们只记录删除,和增加这个字符 x 的最小的费用就好了!->转变成添加多少个字符形成回文串费用最少! 8 9 str[i]!=str[k] 10 dp[i][j]=min(dp[i][j-1]+cost[str[k]-'a

UVA - 11584 划分字符串的回文串子串; 简单dp

/** 链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34398 UVA - 11584 划分字符串的回文串子串: 简单dp 题目大意: 给一个字符串, 要求把它分割成若干个子串,使得每个子串都是回文串.问最少可以分割成多少个. 定义:dp[i]表示前0~i内的字符串划分成的最小回文串个数: dp[i] = min(dp[j]+1 | j+1~i是回文串); 先预处理flag[i][j]表示以i~j内的字符串为回文串

HDU 4632 Palindrome subsequence(区间dp,回文串,字符处理)

题目 参考自博客:http://blog.csdn.net/u011498819/article/details/38356675 题意:查找这样的子回文字符串(未必连续,但是有从左向右的顺序)个数. 简单的区间dp,哎,以为很神奇的东西,其实也是dp,只是参数改为区间,没做过此类型的题,想不到用dp,以后就 知道了,若已经知道[0,i],推[0,i+1], 显然还要从i+1 处往回找,dp方程也简单: dp[j][i]=(dp[j+1][i]+dp[j][i-1]+10007-dp[j+1][

HDU 3068 &amp;&amp;HDU 3294 +最长回文串*3—— manacher/扩展KMP/DP

HDU 3068 http://acm.hdu.edu.cn/showproblem.php?pid=3068 HDU 3294http://acm.hdu.edu.cn/showproblem.php?pid=3294 扩展KMP:https://segmentfault.com/a/1190000008663857 感觉DP相对实现起来更简洁一些,很容易想到,可以用bool dp[i][j]:表示从i到j是否为回文串,然后最长长度用一个变量记录就好 如果dp[i][j]为回文串,那么dp[i

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

POJ 3280 Cheapest Palindrome(区间DP求改成回文串的最小花费)

题目链接:http://poj.org/problem?id=3280 题目大意:给你一个字符串,你可以删除或者增加任意字符,对应有相应的花费,让你通过这些操作使得字符串变为回文串,求最小花费.解题思路:比较简单的区间DP,令dp[i][j]表示使[i,j]回文的最小花费.则得到状态转移方程: dp[i][j]=min(dp[i][j],min(add[str[i]-'a'],del[str[i]-'a'])+dp[i+1][j]); dp[i][j]=min(dp[i][j],min(add[