码迷,mamicode.com
首页 > 其他好文 > 详细

关于回文串的DP问题

时间:2017-08-23 10:38:30      阅读:258      评论:0      收藏:0      [点我收藏+]

标签:cos   sans   方法   stdio.h   name   技术分享   http   rom   getc   

问题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 }
View Code 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     }  
View Code 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     }  
View Code 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 }
View Code 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 }
View Code 2

 

关于回文串的DP问题

标签:cos   sans   方法   stdio.h   name   技术分享   http   rom   getc   

原文地址:http://www.cnblogs.com/Secret-Service/p/7414290.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!