问题描述:
给定一个字符串str, 问最少需要添加多少个字符串能使得str变成回文串?该问题是最长公共子序列的问题的延伸。
定义:
字符串 str 长度为 n, 字符串的逆串 istr。他们的最长公共子序列的长度 l = lcs(str, istr)。公共子序列中的元素不需要添加对称,剩余的元素需要添加一元素使其对称。
所以最少需要添加的字符串的数目 min = n - l。
最长公共子序列的计算:
定义:
lcd[ m ][ n ] 长度为m, n的两个字符串的最长公共子序列的长度。a[0, m-1], b[0, n-1] 两个字符串的形式化表示。
a[m-1] == b[n-1] 该种情况下lcd[m][n] 只有一个子问题
lcd[m][n] = lcd[m-1][n-1] + 1;
a[m-1] != bn-1[] 该种情况下该种情况下lcd[m][n] 有两个子问题
lcd[m][n] = lcd[m][n-1] ; lcd[m][n] = lcd[m-1][n] ; 子问题的选择也很明确:选择最大的子问题。
初始化:
lcd[0][0 ... n] = { 0 }
lcd[0 ... m][0] = { 0 }
递推过程:
for (i = 1; i < m; i++)
for (j = 1; j < n; j++)
if str[i] == istr[j] lcd[i][j] = lcd[i - 1][j - 1] + 1
else lcd[i][j] = max { lcd[i][j - 1], lcd[i - 1][j] }
#include<iostream>
#include<fstream>
using namespace std;
static int n;
static const int N = 5001;
static char str[N];
static char rstr[N];
static int c[N][N];
//#define DEBUG
int lcs()
{
int i, j;
for (i = 0; i <= n; i++)
c[i][0] = 0;
for (i = 0; i <= n; i++)
c[0][i] = 0;
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
if (str[i -1] == rstr[j - 1])
c[i][j] = c[i - 1][j - 1] + 1;
else if (c[i - 1][j] > c[i][j - 1])
c[i][j] = c[i - 1][j];
else
c[i][j] = c[i][j - 1];
}
}
return c[n][n];
}
int main()
{
#ifdef DEBUG
fstream cin("G:\\book\\algorithms\\acm\\Debug\\dat.txt");
#endif
cin >> n;
cin >> str;
int i, j;
for (i = 0, j = n - 1; j >= 0; j--)
{
rstr[i++] = str[j];
}
rstr[i]= '\0';
cout << n - lcs() << "\n";
return 0;
}
上面代码的内存使用超限了,需要进行优化。
#include<iostream>
#include<fstream>
using namespace std;
#define N 5001
static char str[N];
static char rstr[N];
static int c[2][N];
//#define DEBUG
/*
296K 766MS
*/
int main()
{
#ifdef DEBUG
fstream cin("G:\\book\\algorithms\\acm\\Debug\\dat.txt");
#endif
int n;
while(cin >> n)
{
cin >> str;
int i, j;
for (i = 0, j = n - 1; j >= 0; j--)
{ rstr[i++] = str[j]; }
rstr[i]= '\0';
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
if (str[i -1] == rstr[j - 1])
c[i % 2][j] = c[(i - 1) % 2][j - 1] + 1;
else if (c[(i - 1) % 2][j] > c[i % 2][j - 1])
c[i % 2][j] = c[(i - 1) % 2][j];
else
c[i % 2][j] = c[i % 2][j - 1];
}
}
cout << n - c[(i - 1) % 2][j - 1] << "\n";
}
return 0;
}
优化后的代码,使用轮换数组代替整个动态规划表使用空间大大减少。该题要经常复习,该题和poj 1745处理方式类似。