回文串:给一个串,求最少增添几个字符能使之成为回文串
例如:Ab3bd 首尾增添2个d A
成为Adb3bdA 或dAb3bAd
解法:动态规划;
1)和杭电多校一题统计回文串的题很像,考虑能不能直接套用;
2)还是先从动态规划的基本元素分析吧;
设状态:dp[i][j]为第i位与第j位之间的串需要多少个最少字符才能组成回文串
3)尝试写状态方程并判断最优子结构性质,无后效性(有向无环):
Dp[i][j] = dp[i-1][j-1];(s[i]=s[j])
Dp[i][j] = dp[i-1][j]+1
发现dp[i][j]代表未配对的字符的值写方程很困难,那就换一个等价的角度,求最长回文串长度,只要拿总长度减去此值就可以了,状态方程大致如下
Dp[i][j] = Max(dp[i-1][j], dp[i][j-1]);
if(s[i]=s[j])
Dp[i][j] = dp[i-1][j-1] + 2;
问题解决!!!
另外:求回文串也可看做求原串与其逆串的LCS,用滚动数组优化内存,可限制在几百K内。
【源程序1(5000*5000 short型数组)】:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define Max(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
short dp[5005][5005];
char s[5005];
int main(){
//freopen("iofile\\input.txt","r",stdin);
int n,i,j,d;
while(~scanf("%d",&n)){
scanf("%s",s+1);
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
dp[i][i]=1;
for(d=1;d<=n-1;d++){
for(i=1;i+d<=n;i++){
j=i+d;
dp[i][j]=Max(dp[i+1][j],dp[i][j-1]);
if(s[i]==s[j])
dp[i][j]=Max(dp[i][j],dp[i+1][j-1]+2);
}
}
printf("%d\n",n-dp[1][n]);
}
return 0;
}
【源程序2:LCS+滚动数组】
别人的代码,拿过来借鉴一下。
#include <iostream>
#include <cstdlib>
#include <algorithm>
#define N 5010
#define REP(i,n) for(i=0;i<n;i++)
using namespace std;
int main()
{
int i,j,k,n;
cin>>n;
char a[N],b[N];
scanf("%s",a);
REP(i,n) b[i] = a[i];
reverse(a,a+n);
short dp[2][N];
REP(i,n)
{
dp[0][i] = 0;
dp[1][i] = 0;
}
REP(i,n)
{
REP(j,n)
{
if( a[i] == b[j] )
dp[(i+1)%2][j+1] = dp[i%2][j]+1;
else
{
dp[(i+1)%2][j+1] = dp[i%2][j+1];
if( dp[(i+1)%2][j+1] < dp[(i+1)%2][j] )
dp[(i+1)%2][j+1] = dp[(i+1)%2][j];
}
}
}
cout<<n-dp[n%2][n]<<endl;
return 0;
}