#include <iostream>        // DP + 滚动数组
#include <string>

using namespace std;
#define MAXN 5005
int ans[2][MAXN];
int main()
{
int n;
char s[MAXN];
scanf("%d%s",&n,s);
for (int i=n-1;i>=0;--i) // 注意i是从大到小,因为求解ans[i][j]过程需要先知道ans[i+1][j]
{

for (int j=i+1;j<n;++j)
{
if (s[i] == s[j])
ans[i%2][j] = min ( ans[(i+1)%2][j-1] , 1 + min ( ans[(i+1)%2][j] , ans[i%2][j-1] ) );
else
ans[i%2][j] = min ( 1 + ans[(i+1)%2][j] , 1 + ans[i%2][j-1] );
}
}
printf("%d\n",ans[0][n-1]); //相当于ans[0%2][n-1]
return 0;

}

 

 

poj 1159 Palindrome_#includepoj 1159 Palindrome_#include_02 DP
/*
题意: 给定一个字符串S,问至少插入几个字符才能使它变成回文串。

思路: 动态规划 ,ans[I][J]表示在S[I]和S[J]间插入至少几个字符才能使S[I...J]是一个回文串,那么有如下两种情况:

(1).当S[I] = S[J]时,即遇到类似"A****A"的情况,此时可以看做对"****"添加最少的字符使其成为回文串,所以有ans[I][J] = ans[I+1][J-1],
当然,这种可能也有:把S[I+1到J]变成一个回文串,然后再到S[J]后面添加一个‘A’,这种情况也有可能发生,
所以还要判断 1+ans[I+1][J] 和 1+ans[I][J-1] 这两种情况是否更优。

(2).对于S[I] != S[J],情况比较简单,只需考虑把S[I+1到J]变成回文串再在后面添加一个S[I],或者把S[I到J-1]变成回文串再在前面添加一个S[J]

综上,DP方程如下:
当S[I] = S[J]时, ans[I][J] = MIN ( ans[I+1][J-1] , 1 + MIN ( ans[I+1][J] , ans[I][J-1] ) )
当S[I]!= S[J]时, ans[I][J] = 1 + MIN ( ans[I+1][J] , ans[I][J-1] )

*/

#include <iostream> // DP
using namespace std;

#define MIN(X,Y) ( X < Y ? X : Y )
#define MAX 5000
short int ans[MAX][MAX]; //开int类型会超空间MLE,所以用short int
int main()
{
int n,i,j,k;
char s[MAX];
scanf("%d%s",&n,s);
for (i=n-1;i>=0;--i) // 注意i是从大到小,因为求解ans[i][j]过程需要先知道ans[i+1][j]
{
for (j=i+1;j<n;++j)
{
if (s[i] == s[j])
ans[i][j] = MIN ( ans[i+1][j-1] , 1 + MIN ( ans[i+1][j] , ans[i][j-1] ) );
else
ans[i][j] = MIN ( 1 + ans[i+1][j] , 1 + ans[i][j-1] );
}
}
cout<<ans[0][n-1]<<endl;
return 0;
}