POJ1159 Palindrome(最大回文串长度)
回文串:给一个串,求最少增添几个字符能使之成为回文串
例如: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; }