题目描述:
X星球的考古学家发现了一批古代留下来的密码。
这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。
你的任务是:
给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。
输入格式
共一行,包含一个由大写字母ABCD构成的字符串,表示现在看到的密码串。
输出格式
输出一个整数,表示至少脱落了多少个种子。
数据范围
输入字符串长度不超过1000
输入样例1:
ABCBA
输出样例1:
0
输入样例2:
ABDCDCBABC
输出样例2:
3
题解:
给出一段字符串,求至少填充几个字符,能使它构成回文串。
思维:
回文串实际上是以中间为准,左右对应相同的。也就是说,左右有几个不同,就需要增加/删除几个字符使他们相同,所以找到最长的回文字串后,用原来的长度减去回文串长度即是答案。如此,题目就变成了找最长的回文字串。
区间dp:
个人认为区间dp和线性dp不同点在于,区间dp通常需要考虑一个区间的维度(考虑左端点和右端点),由小区间推出大区间。线性dp通常是从一段考虑,只有一个维度。
先枚举区间长度,再枚举左端点,计算出右端点。因为长的区间需要用短的区间来推出。
通常len==1用来初始化。从len=2开始枚举。
len==1时初始化
for(int len=2;len<=n;len++){
for(int l=0;l+len-1<n;l++){
int r = l + len-1;
...
...
}
}
f [ l, r ]表示从l到r的区间内,回文字串的长度的集合,属性是最大值。
状态可以划分为l、r都选,l选r不选,l不选r选,l、r均不选。
边界问题:当区间长度为1时,一个字符自己成长度为1的回文串。
代码:
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int N = 1010;
char s[N];
int f[N][N];
int main(){
scanf("%s", &s);
int n = strlen(s);
for(int len=1;len<=n;len++){
for(int l=0;l+len-1<n;l++){
int r = l + len-1;
if(len==1) f[l][r] = 1;
else{
if(s[l]==s[r]) f[l][r] = f[l+1][r-1]+2;
f[l][r] = max(f[l][r], max(f[l+1][r], f[l][r-1]));
}
}
}
printf("%d", n - f[0][n-1]);
return 0;
}