周日做腾讯笔试。。最后一题是最长回文子序列。。。(●°u°●) 」由于太久没做这样的题了。。第一题蛇形数组坑了我好久。。然而原因居然是控制前进方向的数组写错了。。TAT
然后最后一题就剩下十几分钟。。于是我一看到“回文”,“序列”。。就开始写回文子串。。写完剩2分钟再看一眼题目就发现悲剧了。。(((o(*゚▽゚*)o)))。。。。以前自己刷题也犯这种错误。。但是并不在意。。。到笔试的时候就坑爽了。。。
*************************************************************************************************
最长回文子序列的变型?要求变化的费用最少,其实变化的少也就是子序列要长嘛。。一样的。。
(a的坐标是i,b是j)
对于这样一串序列,如果头尾相等那自然是不用做操作的。如果不相等,一种就是补a,一种就是补b。(删除操作和添加操作实质是一样的,所以在两个操作中挑费用少的那个记录就好)。可以规定添加操作都是添加在序列的两端(也可以在内侧)。这样如果补a的话,就是在b的右边补上a,那么这时候回文序列就是考察序列[i+1,j]这一段区间内的情况了。同理如果补b,就是考察[i,j-1]区间的情况。
用dp[i][j]记录区间[i,j]的最小操作费用。循环的时候注意顺序,[i+1,j],[i,j-1]显然应该是已经出现过的情况。
观察可发现dp可以使用滚动数组。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 2010
#define maxm 10010
char s[maxn];
int a[256];
int dp[2][maxn];
int min(int x,int y){return x<y?x:y;}
int main()
{
int n,m;
while(cin>>m>>n)
{
cin>>s;
while(m--)
{
char tmp[5];
int x,y;
cin>>tmp>>x>>y;
a[tmp[0]]=min(x,y);
}
memset(dp,0,sizeof(dp));
for(int i=n-1;i>=0;i--)
{
int flag=i%2;
for(int j=i+1;j<n;j++)
{
if(s[i]==s[j]) dp[flag][j]=dp[!flag][j-1];
else
dp[flag][j]=min(dp[flag][j-1]+a[s[j]],dp[!flag][j]+a[s[i]]);
}
}
cout<<dp[0][n-1]<<endl;
}
return 0;
}