POJ_1159 Palindrome (线性动态规划+滚动数组)
题意是说,给定一个字符串,问至少还需要插入多少个字符才能使得该字符串成为回文字符串。
这道题一开始做的时候用了一个简单的动态规划,开了一个5000*5000的数组,用递归形式实现,代码如下:
其中d[i][j]表示i到j所需要插入的字符数。然而数组开得太大直接报MLE。因此想到用滚动数组来解决。
MLE代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; //内存超限 MLE const short maxn=5000+1; int d[maxn][maxn];//d表示i到j形成回文串所需加入的字符数 string s; int dp(int i,int j){ if(d[i][j]>=0)return d[i][j]; if(i<j){ if(s[i]==s[j]){ d[i][j]=dp(i+1,j-1); return d[i][j]; } else{ d[i][j]=min(dp(i+1,j),dp(i,j-1))+1; return d[i][j]; } } else if(i==j){ d[i][j]=0; return 0; } } int main(void){ int n; scanf("%d",&n); cin>>s; memset(d,-1,sizeof(d)); int ans=dp(0,n-1); cout<<ans<<endl; return 0; }
使用滚动数组的动态规划和上述略有不同。基本算法如下:
设输入为X,则记X的逆序列为Y,其中X的字符个数为n,
那么ans=n-(X和Y的最大公共子序列长度)。这样就将该问题转化为了求最长公共子序列的问题。
则此时令d[i][j]表示为X序列和Y序列在前I项和前j项的最大公共序列的长度。
具体状态转移方程见代码或最长公共子序列题的动态转移方程。
但此时的d[i][j]仍然是5000*5000的数组,而由状态转移方程可以发现,在递推过程中其实只涉及两层数组内容,因此可以只保留两层数组从而将数组减小为2*5000的滚动数组。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; //线性动态规划,滚动数组 const int maxn=5000+5; int d[2][maxn]; int main(void){ int n; while(cin>>n){ string s1,s2; cin>>s1; s2=s1; reverse(s1.begin(),s1.end()); memset(d,0,sizeof(d)); int n=s1.length(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(s1[i-1]==s2[j-1]){ d[i%2][j]=d[(i-1)%2][j-1]+1; } else{ d[i%2][j]=max(d[(i-1)%2][j],d[i%2][(j-1)]); } } } cout<<n-d[n%2][n]<<endl; } return 0; }