题目描述
X星球的考古学家发现了一批古代留下来的密码。 这些密码是由几种植物的种子串成的序列。植物种类由26个大写英文字母表示,即最多有26种植物。 仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。 由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。
考古学家给kekao的任务是: 给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。
输入格式:
输入一行,表示现在看到的密码串(长度不大于1000)
输出格式:
要求输出一个正整数,表示至少脱落了多少个种子。
输入样例:
在这里给出一组输入。例如:
ABCBA
输出样例:
在这里给出相应的输出。例如:
0
输入样例:
在这里给出一组输入。例如:
ABDCDCBABC
输出样例:
在这里给出相应的输出。例如:
3
参考代码
这里给出一种动态规划的做法,要求原来的序列,即先求出与倒序的字符串匹配的最长子序列长度(匹配了就说明对称),然后用字符串总长度减去最长公共子序列即可。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#include <queue>
#include <stack>
using namespace std;
#define N_MAX 1000
char a[N_MAX+5];
char b[N_MAX+5];
int dp[N_MAX+5][N_MAX+5];
void print()
{
int k1=1;
cout<<" ";
for(k1 = 1;k1<=9;k1++)
{
cout<< k1 <<" ";
}
cout << endl;
for(int i = 1;i<=9;i++)
{
cout << i << " ";
for(int j = 1;j<=9;j++)
{
cout << dp[i][j] << " ";
}
cout <<endl;
}
cout << "--------------------"<<endl;
}
int main()
{
freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
int i,j;
int len;
//memset(dp,1,sizeof(dp));
scanf("%s",a+1);
len = strlen(a+1);
for(i = 1;i<=len;i++)
{
b[i] = a[len-i+1];
}
b[len+1] = 0;
for(i = 1;i<=len;i++)
{
for(j = 1;j<=len;j++)
{
if(a[i]==b[j])
dp[i][j] = dp[i-1][j-1] + 1;
else
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
//print();
}
}
cout << len - dp[len][len];
return 0;
}
一种错误的做法
在这里有必要指出一种基于贪心的做法,这是错误的。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
using namespace std;
char str1[1005],str2[1005];
int main()
{
freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
int head,tail;
int count_ = 0;
cin >> str1;
head = 0;
tail = strlen(str1) - 1;
while(head<tail)
{
while(str1[head]==str1[tail]&&head<tail)
{
head++;
tail--;
}
if(head==tail)
break;
count_++;
tail--;
}
cout << count_;
return 0;
}
为什么会出错?
贪心是每一步取当前最优,推出全局最优,且没有后效性。
在这个示例中,可以发现:
1.使用前后指针匹配,认为“在前指针head指向的每一个字母必须先和后指针tail指向的字母匹配,否则tail–”
2.每一个字母使用后不能再次使用,因此可能会错过最优值
3.仅一次局部最优非全局最优
4.如何优化这种做法?多次轮流匹配以求最优。