1. 自己思考思路的时候开始觉得用动态规划,但是想不出公式。后来觉得如果把输入的字符串反过来,然后和原来的字符串相比求最大序列,然后答案就是原来的序列减去最大序列了。因为无法匹配的肯定需要添加。可以匹配的话就一定会出现在最大序列里面。这可以用反证法证明。解决了方法的问题。下面就是代码。其实就是一个最大序列匹配的问题。也是动态规划,公式:dp[i][j] = max{dp[i][j-1],dp[i-1][j],dp[i-1][j-1]}
但是内存超时。所以,需要优化。
2. 优化空间,因为5000*5000确实比较大,所以就想到可以用一维。上面公式里面因为新的值只和相邻的三个值有关。新的公式是,dp[i] = max{dp[i],dp[i-1], dp[i-1][j-1]};这里可以发现,dp[i]是上次更新的值,dp[i-1]是刚刚更新的值。只有dp[i-1][j-1]无法表示。所以,就用一个值来记录一下就可以了。于是有了下面的代码:一次ac。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
//变量和要用到的数据
const int maxn = 5005;
char s[maxn];
char s2[maxn];
//int dp[maxn][maxn];
int dp[maxn][maxn];
int n;
int big (int temp1, int temp2, int temp3)
{
int result = temp1;
if(result<temp2)
result = temp2;
if(result<temp3)
result=temp3;
return result;
}
int value(char a,char b)
{
if(a==b)
return 1;
return 0;
}
int main()
{
freopen("input.txt","r",stdin);
while(~scanf("%d",&n))
{
scanf("%s",s+1);
for(int i = 1; i<=n; i++)
for(int j = 1; j<=n; j++)
{
dp[i][j] = 0;
}
//想到了解法,那就是把字符串倒过来和原来的字符串比较,取最长公共子序列。然后再三个字符串一起比较。
for(int i = 1; i<=n; i++)
{
s2[n-i+1] = s[i];
}
for(int i = 1; i<=n; i++)
{
for(int j = 1; j<=n; j++)
{
dp[i][j] = big(dp[i-1][j-1]+value(s[i],s2[j]),dp[i][j-1],dp[i-1][j]);
}
}
cout << (n-dp[n][n])<<endl;
}
}
但是内存超时。所以,需要优化。
2. 优化空间,因为5000*5000确实比较大,所以就想到可以用一维。上面公式里面因为新的值只和相邻的三个值有关。新的公式是,dp[i] = max{dp[i],dp[i-1], dp[i-1][j-1]};这里可以发现,dp[i]是上次更新的值,dp[i-1]是刚刚更新的值。只有dp[i-1][j-1]无法表示。所以,就用一个值来记录一下就可以了。于是有了下面的代码:一次ac。
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
//变量和要用到的数据
const int maxn = 5005;
char s[maxn];
char s2[maxn];
//int dp[maxn][maxn];
int dp[maxn];
int n;
//求三者之间的最大值
int big (int temp1, int temp2, int temp3)
{
int result = temp1;
if(result<temp2)
result = temp2;
if(result<temp3)
result=temp3;
return result;
}
//对比两个字符
int value(char a,char b)
{
if(a==b)
return 1;
return 0;
}
int main()
{
//freopen("input.txt","r",stdin);
while(~scanf("%d",&n))
{
//输入和初始化
scanf("%s",s+1);
for(int j = 0; j<maxn; j++)
{
dp[j] = 0;
}
//想到了解法,那就是把字符串倒过来和原来的字符串比较,取最长公共子序列。然后再三个字符串一起比较。
for(int i = 1; i<=n; i++)
{
s2[n-i+1] = s[i];
}
//最大公共字符串。不过在经典方法上改进了。因为不改进的话内存超了。因为实际纸盒上次的更新有关。所以可以优化到一维。但是和完全背包问题有
//有所不同的是,还需要对角的值,这个需要单独存储。就是下面的tempValue值。
int tempValue = 0;
for(int i = 1; i<=n; i++)
{
tempValue = 0;
for(int j = 1; j<=n; j++)
{
//if(s[i]==s2[j])
int tempValue2 = dp[j];
dp[j] = big(tempValue+value(s[i],s2[j]), dp[j-1],dp[j]);
tempValue = tempValue2;
}
}
//输入结果
cout << (n-dp[n])<<endl;
}
}