Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 48269 | Accepted: 16570 |
Description
As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome.
Input
Output
Sample Input
5 Ab3bd
Sample Output
2
Source
假设dp[i][j]表示字符串a[i..j]变成palindrome
至少需要花dp[i][j]个操作(i<=j)
那么
如果a[i]=a[j],那么dp[i][j]=dp[i+1][j-1]
否则,就是dp[i][j] = min(dp[i-1][j], dp[i][j-1]) +1 这是因为要在前面或者后面补一个数
初始化为dp[i][i] = 0, dp[i][i+1]= a[i]==a[i+1] ? 0: 1
两种写法:
递归:
int getdp(int i,int j){
if(i == j) return 0;
if(i == j - 1) return a[i]==a[i+1] ? 0 : 2
if(dp[i][j] != -1) return dp[i][j]
if(a[i]==a[j]) return dp[i][j]=getdp(i + 1,j-1)
else return dp[i][j]=min(getdp(i, j-1),getdp(i+1, j))+1
}
递推(i为间隔,间隔小的开始递归)
for(int i = 1;i<= n;i++){
for(int j = 0;j < n - i;j++){
if(a[j]==a[i+j]) dp[j][i+j] =dp[j+1][i+j-1]
else dp[j][i+j]=min(dp[j+1][i+j],dp[j][i+j-1])+1
}
}
注意dp[i][j]的范围i<=j
感想:
1、如果这个题换一种思路还可以用
假设串s和它的逆串s'的最长公共子序列长度为L.
则需要添加的字符个数为 串长s-L。
2、除了用short避免MLE,还可以用dp中常出现的滚动数组或者动态滚动数组
来节省内存。
3、不过递归容易爆栈,斟酌着用。
4、终于搞清楚为什么有时候写的程序用c++提交AC,而用g++提交就wa。
要从计算机原理说起:
内存实际上连续的一片,申请内存的时候,一些语言会强制检查越界,
比如java。c++这个实际上数据就是一个指针,比如dp[0]就是*dp,
dp[1]就是*(dp+1)。如果dp-1这块内存恰好没被使用,那么dp[-1]就不会
报错了。
如果一个二级数组dp的定义是dp[2][4]的话,那么
dp[1][-1]实际上就是dp[0][3],一些编译器会报错,一些不会。
在内存中,二级数据是这么存的:
dp[0][0],dp[0][1],……,dp[0][M-1],dp[1][0],dp[1][1],……
在编译器中,实际上会数组访问这么定位
dp[i][j] = *(dp+i*M+j)
5、最有趣的是我开始把dp的初始条件弄错了,弄成
dp[i][i+1]= (a[i]==a[i+1]?0:2)了,结果用递推写AC了,用递归
却是wa.要不是因为递归wa了,我还不会发现我这里错了。。
因为要加入的字符最少,所以尽管
ab可以加两个字符使之变成abba、bab和aba.但是应该至少是加一个字符。
递推AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
short dp[5005][5005];
char a[5010];
int main()
{
int n,i,j;
while(scanf("%d",&n)!=EOF)
{
getchar();
gets(a);
for(i=0;i<n;i++)
{
dp[i][i]=0;
dp[i][i+1]=(a[i]==a[i+1]?0:1);
}
for(i=1;i<=n;i++)
{
for(j=0;j<n-i;j++)
{
if(a[j]==a[j+i])
dp[j][j+i]=dp[j+1][j+i-1];
else
dp[j][j+i]=min(dp[j][j+i-1],dp[j+1][j+i])+1;
}
}
printf("%d\n",dp[0][n-1]);
}
return 0;
}
//AC
//1407MS
递归AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
char a[5005];
short dp[5005][5005];
short getdp(int i,int j)
{
if(i==j) return 0;
if(i+1==j) return a[i]==a[j]?0:1;
if(dp[i][j]!=-1) return dp[i][j];
if(a[i]==a[j]) return dp[i][j]=getdp(i+1,j-1);
else return dp[i][j]=min(getdp(i,j-1),getdp(i+1,j))+1;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
getchar();
gets(a);
memset(dp,-1,sizeof(dp));
printf("%d\n",getdp(0,n-1));
}
return 0;
}
//AC
//1438MS