Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 45704 | Accepted: 15580 |
Description
Input
Output
Sample Input
5 Ab3bd
Sample Output
2
Source
题意:给你一个字符串,你可以在这个字符串的任意位置插入一个字符,现在要求最少要插入多少个字符才能使这个字符串变成回文的。
分析:很经典的DP,在刘汝佳的书里也应该有提到这题。这题是我AC的第一个区间dp题,AC这题后自己也对区间dp有了一定的了解。好了,废话不多说了,现在开始讲解法:
对于一个字符串序列S1,S2,S3……Sn 想要将它变成回文的,注意!!不要想得太复杂,其实我们只能在字符串的两端添加字符(这儿要仔细想想,想通了,这题就变得容易了),在字符串当中添加字符是完全没有意义的,所以对于当前的状态,我们只可能遇到2种可能的情况,我们来归纳一下:
1.当S1==Sn时(字符串头字符和字符串尾部字符相等时),我们的任务便转换为了将S2,S3,S4……S(n-1)变成回文,对吗?
2.当S1!=Sn时,我们又有了两种决策
第一种决策:在字符串序列S1,S2,S3……Sn 的左边添加一个字符,我们设这个字符为Si,使它等于Sn,这样我们就将当前的任务转化为了将S1,S2,S3……S(n-1)变成回文字符串。
第二种决策:在字符串序列S1,S2,S3……Sn 的右边添加一个字符,我们设这个字符为Sk,使他等于S1,这样我们就将当前的任务转化为了将S2,S3,S4……Sn变成回文字符串。
根据这个思路,我们很快就能写出递归代码(我加了记忆化):
int DFS(int l,int r){ //l和r表示:从当前字符串的第l个字符到当前字符串的第r个字符变成回文字符串所需要的最小代价
if(l>=r) return 0;
if(dp[l][r]!=-1) //记忆化
return dp[l][r];
int M=99999999;
if(str[l]==str[r]) //第一种情况
M=DFS(l+1,r-1);
else{ //第二种情况
M=min(M,DFS(l,r-1)+1);
M=min(M,DFS(l+1,r)+1);
}
return dp[l][r]=M; //记忆化
}
不过我们遇到了一个很严重的问题:字符串的长度是一个小于5000的数字,但是要是字符串的长度到达了5000,我们岂不是要开一个5000*5000的数组吗?一开始我对这个问题也没什么办法,最后听dicuss中说可以把数组的类型开到short就能过,改了以后就过了。当然,这不是正解,正解是用滚动数组,可以将空间从O(n^2)优化到O(n),不失为一个好办法。不过我写得是记忆化DFS,所以不能用滚动数组。
我代码(1922ms AC):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <string>
using namespace std;
char str[5005];
short int dp[5005][5005];
int DFS(int l,int r){
if(l>=r) return 0;
if(dp[l][r]!=-1)
return dp[l][r];
int M=99999999;
if(str[l]==str[r])
M=DFS(l+1,r-1);
else{
M=min(M,DFS(l,r-1)+1);
M=min(M,DFS(l+1,r)+1);
}
return dp[l][r]=M;
}
int main()
{
int len;
while(scanf("%d",&len)!=EOF){
scanf("%s",str+1);
memset(dp,-1,sizeof(dp));
cout<<DFS(1,len)<<endl;
}
return 0;
}
照着原样写了个,惭愧。。。
//Accepted 49400K 1516MS C++ 599B 2013-03-15 19:19:43 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 5000 + 5; short int dp[maxn][maxn]; char str[maxn]; int dfs(int l, int r) { if(l >= r) return 0; if(dp[l][r] != -1) return dp[l][r]; int M; if(str[l] == str[r]) { M = dfs(l+1, r-1); } else { M = min(dfs(l, r-1) + 1, dfs(l+1, r) + 1); } return dp[l][r] = M; } int main() { int n; while(scanf("%d", &n) != EOF) { memset(str,'0',sizeof(str)); memset(dp,-1,sizeof(dp)); scanf("%s", str+1); printf("%d\n", dfs(1,n)); } return 0; }