密码脱落(区间dp,最长公共子序列)

  • Post author:
  • Post category:其他




密码脱落


题目链接


X星球的考古学家发现了一批古代留下来的密码。

这些密码是由A、B、C、D 四种植物的种子串成的序列。

仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。

由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。

你的任务是:

给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。



输入格式

共一行,包含一个由大写字母ABCD构成的字符串,表示现在看到的密码串。



输出格式

输出一个整数,表示至少脱落了多少个种子。



数据范围

输入字符串长度不超过1000



输入样例1:

ABCBA



输出样例1:

0



输入样例2:

ABDCDCBABC



输出样例2:

3



算法分析

将当前字符串加上若干字母使其变为回文串,其实就是将多余的字母对应位置加上对应的字母.

所以这题也可以转化为删除多少字符使其变为回文串.

一种方法是将字符串倒转,求出他的最长公共子序列,这样就可以比较便利地求出来,最长组成回文串子序列的长度.

另一种是区间dp的方式,我们的思路是从两端向中间靠拢,选判断边界相等的问题.

如果边界两个字符是相等的,那么直接当前区间的最长回文串子序列+2,

如果是不相等的情况下,就要判断是包含左端点不包含右端点的长度长,还是包含右端点不包含左端点的长度长,

这一点的思路是和最长公共子序列的思路是一样的.

当然还有左右端点都不选的状态,但是选的话一定是大于等于的状态,所以就不用考虑都不选的情况了

所以转移方程就是

if(s[l]==s[r]) dp[l][r]=dp[l+1][r-1]+2;
else
{
      dp[l][r]=max(dp[l+1][r],dp[l][r]);
      dp[l][r]=max(dp[l][r-1],dp[l][r]);
}



代码实现

区间dp求最长回文子序列

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e3+5;
int dp[maxn][maxn];
int main()
{
    string s;
    cin>>s;
    int slen=s.size();
    for(int len=1;len<=slen;len++)
    {
        for(int l=0;l+len-1<slen;l++)
        {
            int r=l+len-1;
            if(l==r)    dp[l][r]=1;
            else if(s[l]==s[r]) dp[l][r]=dp[l+1][r-1]+2;
            else
            {
                dp[l][r]=max(dp[l+1][r],dp[l][r]);
                dp[l][r]=max(dp[l][r-1],dp[l][r]);
            }
        }
    }
    cout<<slen-dp[0][slen-1]<<endl;
    return 0;
}

将字符串反转求最长回文子序列

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e3+5;
int dp[maxn][maxn];
int main()
{
    string s;
    cin>>s;
    int slen=s.size();
    string a=s;
    reverse(a.begin(),a.end());
    for(int i=1;i<=slen;i++)
    {
        for(int j=1;j<=slen;j++)
        {
            if(a[i-1]==s[j-1])
                dp[i][j]=dp[i-1][j-1]+1;
            else
            {
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    cout<<slen-dp[slen][slen]<<endl;
    return 0;
}

DFS做法,不间接求,直接是求使字符串变回文的思路,不过还是区间dp的思想


参考题解

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e3 + 5;

int f[N][N];
string str;

int dfs(int l, int r)
{
    if (f[l][r]) 
        return f[l][r];
    if (l >= r)
        return 0;
    int res;
    if (str[l] != str[r])
        res = min(dfs(l, r-1)+1, dfs(l+1, r)+1);
    else
        res = dfs(l+1, r-1);
    return f[l][r] = res;
}

int main()
{
    cin >> str;
    cout << dfs(0, str.size()-1);
    return 0;
} 



版权声明:本文为weixin_47701814原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。