poj 1159 回文词原题描述 http://acm.pku.edu.cn/JudgeOnline/problem?id=1159
回文词是一种对称的字符串。任意给定一个字符串,通过插入若干字符,都可以变成回文词。次题的任务是,求出将给定字符串变成回文词所需要插入的最少字符数。
比如 “Ab3bd”插入2个字符后可以变成回文词“dAb3bAd”或“Adb3bdA”,但是插入少于2个的字符无法变成回文词。
每当遇到一个题目就要分析它的特点。对于这个题目来说,我们设所给的字符串是串s=a1a1...an,我们的目的是通过在S上添加尽量少的字母使其达到回文的要求,我们设这种添加最少的字符之后形成的回文字符串是S'=b1b2...b(n+m),我们所求的这个最小数就是m
在字符串 s'里有bi=b(n+m-i+1)。
那么我们就得找s中的这两类字符串:
A:s'中与此类字符串对称的字符出现在s'-s中
B:s'中与此类字符串对称的字符出现在s中
看这个例子
把Ab3bd扩充成
d A b 3 b A d(红色的字母是添加的)
1 2 3 4 5 6 7
位置2,7的对称位置的字符不属于S,
故A={2, 7}
位置3,4,5的对称位置的字符属于S,
故B={3, 4, 5}
这两类字符串的数量和是n,其中A类字符的数量(用|A|表示)就是我们所求的,而我们可以通过n-|B|求得,那么我们的目标就是找|B|了,B字符串有什么特点呢?就是B本身就是一个回文串,回文串又有什么特点呢?就是回文串B是字符串S和它的反向串的LCS(最长公共子串),那么就把这个问题转化成了求LCS的动态规划问题。(以上内容来自:http://blog.csdn.net/cugbliang/archive/2008/08/06/2778646.aspx)
AC代码:
/*
http://acm.pku.edu.cn/JudgeOnline/problem?id=1159
这个题是给我们一个字符串,插入最少的字符让它变成为一个回文字符。
也就是让求它的逆序的最大公共子串(与poj 上1458 是完全一样,只是那个表述更明显些);
所以我们只要去找出与逆序的最大公共子串数,再用 字符长度 减去得到的 最大公共子串数 就是答案了。
str1[i]用来存原串,str2[j] 用来存逆串,没m[i][j]表示str1的前i个与str2的前j个匹配到的最大子串个数。
求最大公共子串的长度递推式:
if(str1[i]==str2[j]) m[i][j]=m[i-1][j-1]+1; //m数组初始化为0
else m[i][j]=min{ m[i-1]]j] || m[i][j-1] }
*/
#include<iostream>
using namespace std;
int N;
char ch1[5002],ch2[5002];
short Min[5002][5002];
void Init()
{
memset(Min,0,sizeof(Min));
cin>>N;
for(int i=1;i<=N;i++)
{
cin>>ch1[i];
ch2[N-i+1]=ch1[i];
}
}
int LCS() //返回最大公共子串的长度
{
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
if(ch1[i]==ch2[j])Min[i][j]=Min[i-1][j-1]+1;
else if(Min[i-1][j]>=Min[i][j-1])Min[i][j]=Min[i-1][j];
else Min[i][j]=Min[i][j-1];
}
return Min[N][N];
}
int main()
{
Init();
cout<<N-LCS()<<endl;
return 0;
}