POJ 1159 Palindrome [动态规划+滚动数组]
写在最前面:吹爆滚动数组!!!!!!!太好用了叭!!!快学它!!!
题目:http://poj.org/problem?id=1159
题目大意:求最少增加几个字符可以使字符串s变成回文串
样例:
输入:5
Ab3bd
输出:2
思路:动态规划
设置状态:f[i][j]的值表示从s[i]到s[j]至少增加几个字符可以使s[i…j]变成回文串
那么我们可以发现s[i]==s[j] 成立时,f[i][j]=f[i+1][j-1],只要使s[i+1…j-1]变成回文串即可
如果s[i]==s[j]不成立,可以使s[i+1…j]变成回文串后,再在s[j]后面增加一个s[i]的字符;同理,也可以使s[i…j-1]变成回文串后,再在s[i]前面增加一个s[j]的字符
f[i][j] = min(f[i+1][j],f[i][j-1])+1;
最终的结果为f[1][n];
状态转移:
if (s[i] == s[j])
f[i][j] = f[i+1][j-1];
else f[i][j] = min(f[i+1][j], f[i][j-1]) + 1;
注意点:
要注意一下下标i,j的循环顺序
由于计算f[i]是要计算f[i+1],所以i要从后往前枚举
由于计算f[j]是要计算f[j-1],所以j要从前往后枚举
for (int i(n); i >= n; --i)
for (int j(i); j <= n; ++j)
完整代码:
//POJ1159
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 5123;
char s[MAXN];
int f[2][MAXN];
int main()
{
int n;
scanf ("%d", &n);
for (int i(1); i <= n; ++i)
scanf (" %c", &s[i]);
for (int i = n; i >= 1; --i)
for (int j = i; j <= n; ++j)
{
if (s[i] == s[j])
f[i][j] = f[i+1][j-1];
else f[i][j] = min(f[i+1][j], f[i][j-1])+1;
}
printf ("%d\n", f[1][n]);
return 0;
}
提交代码!
AC!
等等!
好像
AC
MLE!
那我们就来算一下空间复杂度叭
512351234/1024 = 102520KB
(和f数组相比,s数组过小,可以忽略不计;
一个int,4个字节)
然而,Memory Limit: 65536KB
妥妥地超了
所以那我们该怎么办呢?
别急!
还记得我写在最前面的话么???!!!
吹爆滚动数组!!!
这个时候滚动数组上场了!!!
我们发现计算f[i][k]时只用到了f[i][k]或f[i+1]f[k](k为任意j值);
而i是顺序循环只做一遍,循环完就不要了
也就是说只需要保留上一组数据即可
那么将数组f[i][j]定义为f[2][MAXN]
根据i的奇偶性进行存储
if (s[i] == s[j])
f[i%2][j] = f[(i+1)%2][j-1];
else f[i%2][j] = min(f[(i+1)%2][j], f[i%2][j-1])+1;
是不是空间一下子小了5678910倍???!!!
还是算一下叭
512324/1024 = 40KB
妥妥地没有超
是不是超棒!!!
好了,over