蓝桥杯-密码脱落(最长公共子序列)

【问题描述】

X星球的考古学家发现了一批古代留下来的密码。
这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。

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

输入一行,表示现在看到的密码串(长度不大于1000)
要求输出一个正整数,表示至少脱落了多少个种子。

例如,输入:

ABCBA

则程序应该输出:

0

再例如,输入:

ABDCDCBABC

则程序应该输出:

3

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 3000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

方法一:

首先输入字符串s,令i = 字符串开端,j = 字符串末尾,

如果  s[\ i\ ] == s[\ j\ ], 那就i++ , j--.

如果  s[\ i\ ]\ != s[\ j\ ] ,两种办法: i 左边补上s[j], j--;  j 右边补上s[i] ,i++;

其实没必要真的去补,比如,i 左边补s[j]时,我们可以假设补上了,只需要让j--,i 不变。

 蓝桥杯-密码脱落(最长公共子序列)_第1张图片

这种思路可以用递归实现。

但是这种方法的的时间复杂度是很高的,比如字符串为ABCDEF,那要补成ABCDEFEDCBA

由此可见,最坏的情况下,最深要搜索到第s.size()层,时间复杂度为。

所以当字符串长度为30的时候,时间复杂度  ≈  10 * 10^8,就要差不多10秒才能跑出来。

但是此题字符串长度最长为1000,这种方法就不可取了,但是分而治之的想法是很好的。

经测试,这种方法能得43分。

#include 
using namespace std;
string s;
int dfs(int left, int right)
{
    while (s[left] == s[right])
    {
        left++;
        right--;
    }
    if (left >= right)
        return 0;
    return min(dfs(left + 1, right) + 1, dfs(left, right - 1) + 1);//返回两条支路中,短的那条
}
int main()
{
    cin >> s;
    cout << dfs(0, s.size() - 1) << endl;
    //system("pause");
    return 0;
}

方法二:

① 把原字符串倒转过来

蓝桥杯-密码脱落(最长公共子序列)_第2张图片

② 从s1中的A开始, 在s2中 找对应的 A

蓝桥杯-密码脱落(最长公共子序列)_第3张图片

③ 这时候,如果让这两个A对称,可以把 CB 补到原字符串的前面

蓝桥杯-密码脱落(最长公共子序列)_第4张图片

④ 然后到s1中的B,在s2中找对应的B

蓝桥杯-密码脱落(最长公共子序列)_第5张图片

直接就找到了,说明两个B的位置是对称的,继续

⑤接下来是D,

蓝桥杯-密码脱落(最长公共子序列)_第6张图片

可以发现,在s2中,隔着一个C,才能要找到D。

说明要想两个D对称,要在把C补到 D前面。

蓝桥杯-密码脱落(最长公共子序列)_第7张图片这时候s1已经是回文串了,一共补了三个字母。

 

这是用上找下,我们也可以用下找上,

先找,蓝桥杯-密码脱落(最长公共子序列)_第8张图片

 

 

然后补,蓝桥杯-密码脱落(最长公共子序列)_第9张图片 ,因为s2是s1的翻转,为了方便观察,也可以补在s2的前面,是一样的蓝桥杯-密码脱落(最长公共子序列)_第10张图片

再找,蓝桥杯-密码脱落(最长公共子序列)_第11张图片

再补,蓝桥杯-密码脱落(最长公共子序列)_第12张图片,s2已经是回文串了,补了5个字母;

 

当然,也不用一味地用上找下,或者用下找上,也可以交叉着来

比如:

第一步用下找上

蓝桥杯-密码脱落(最长公共子序列)_第13张图片

下一步可以这样用用上找下:蓝桥杯-密码脱落(最长公共子序列)_第14张图片

蓝桥杯-密码脱落(最长公共子序列)_第15张图片 s2变成了回文串,用了8个字母。

那怎么样找,才是最优解呢,

不断尝试后,我们发现了规律:

蓝桥杯-密码脱落(最长公共子序列)_第16张图片

找到s1和s2的最长公共子序列,

蓝桥杯-密码脱落(最长公共子序列)_第17张图片配不上的CB和C,我们把它挪上去。

蓝桥杯-密码脱落(最长公共子序列)_第18张图片这个就是正确答案了。

s1 和 s2 的 最长公共子序列的长度 lcs = 7 , 然后 字符串的长度 len = 10, len - lcs = 3 就是正确答案。

 

问题就转换成了求最长公共子序列:

#include 
#include 
#include 
using namespace std;
int dp[1005][1005];
int main()
{
    string s1, s2;
    cin >> s1;
    s2 = s1;
    reverse(s2.begin(), s2.end());

    int len = s1.size();
   
    for (int i = 1; i <= len; i++)
    {
        for (int j = 1; j <= len; j++)
        {
            if (s1[i - 1] == s2[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 << len - dp[len][len] << endl;
    //system("pause");
    return 0;
}

 

方法一虽然不能AC,但是在考场上应该是较为容易想到的,

方法二虽然能AC,但是要想到这是一个最长公共子序列的题目,对于一般人而言,,应该是很难想到的吧。。。

你可能感兴趣的:(其他题目,c++,蓝桥杯)