POJ 1159 Palindrome 题解

原题链接

题意:给出一个字符串,求最少再添加几个字符才能使其成为一个回文串
输入第一行是字符串长度,第二行是字符串
数据范围:字符串长度<=5000

Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 58161 Accepted: 20177

最初的想法:

把给定的字符串a翻转变成b,以”ABCBD”为例,转换为”DBCBA”,然后套模板求个最长公共子串,就是最多能够匹配上的字符个数,然后用len一减就是要添加的字符数了。最长公共子串就简单DP即可,dp[i][j]表示用a的前i个字符和b的前j个字符所能构成的最长公共子串,转移方程如下:
dp[i][j]={
if(a[i]==b[j]) dp[i-1][j-1]+1
else max(dp[i-1][j],dp[i][j-1])
}
边界:i=0或j=0时dp[i][j]=0

编者一开始没有注意到内存要求,于是直接int二维数组,当然就/(ㄒoㄒ)/~~
于是开始思考解决方法:
想到可以把int换成short,然后内存过了,但是时间爆了。
又想到也许可以改成滚动数组(int dp[2][5005]),然后就AC了。

代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main(){
    int m;
    string a,b="";
    cin>>m>>a;
    for(int i=m-1;i>=0;i--)
        b+=a[i];
    int dp[2][m+2];
    memset(dp,0,sizeof(dp));
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++){
            if(a[i]!=b[j])
                dp[i%2][j]=max(j ? dp[i%2][j-1] : 0,dp[(i+1)%2][j]);
            else dp[i%2][j]=j ? dp[(i+1)%2][j-1]+1 : 1;
        }
    cout<<m-dp[(m-1)%2][m-1];
    return 0;
}

补充一下,因为人懒直接用的string读入,所以下标从0开始,在数组里就没有边界了。不过i是在mod 2下运算,所以如果用(i+1) mod 2,可以调到另一行,初始状态全是0,刚刚好。但j就得特判一下了,用了两个判断句解决了。

后来的想法:
其实不是我的,是我现在左边坐着的仁兄(LYD)教给我的。
可以用正向思维(管他那个算正向呢),从两边开始搜索,直接考虑在哪些情况下需要新添加一个字符,哪些情况下不用。也是DP动归,不过时间复杂度好像小一点(指的是实际上的,应该不到O(n^2),因为数组基本跑不完),感觉非常好,就是我人太笨想了好久。转移方程:

dp[h][t]={ //head,tail
if(h==t) 0; //这就是搜索完了,当然就不用再新添加字符了
if(h+1==t){ //表示头和尾是挨着的
if(a[h]==a[t]) 0; //两个挨着的字符相同,那么可以直接以他们中间为中心,那么他们两个就匹配上了,所以不用新添加。而且下面的递归方程保证了所有的搜索都是以(h+t)/2为中心的(感性的理解一下)…
else 1; //否则就要添加一个新的字符来让两边匹配上
}
else if(a[h]==a[t]) DP(h+1,t-1); //这两个字符没有挨着,可有相同,所以就让他们匹配了吧,然后往内递归
else min(DP(h+1,t),DP(h,t-1))+1; //这俩儿实在是凑不成一对儿,那就搜索下左右分别向内进一吧,然后毫无疑问是要多添加一个字符了

然后是代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
string a;
const int m=5005;
short dp[m][m];
int DP(int h,int t){
    if(~dp[h][t])
        return dp[h][t];
    if(h==t)
        return dp[h][t]=0;
    if(h+1==t){
        if(a[h]==a[t])
            return dp[h][t]=0;
        else 
            return dp[h][t]=1;
    }
    if(a[h]==a[t])
        return DP(h+1,t-1);
    return dp[h][t]=min(DP(h+1,t),DP(h,t-1))+1;

}
int main(){
    int n;
    cin>>n>>a;
    memset(dp,-1,sizeof(dp));
    cout<<DP(0,n-1);
    return 0;
}

然后为了不爆内存用的是short数组,够使没问题
其他就没有什么好补充的了,看明白DP方程就很好懂了

在POJ上第一个方法跑了1907MS,第二个跑了1782MS,时间都没问题
再次感谢左边的LYD兄,还有右面一起奋战此题的SXY童鞋的支持

By YOUSIKI

你可能感兴趣的:(poj)