同C++之家
下周EAST要放电了,今天在控制大厅整了一天,排放一体机、标记MAC地址、无线换有线、扎线......各种杂货,晚上回来看了下题目,开始被20分吓到了,没想到好的解法,遂又去干杂活,帮同学找了套无线套装,然后拆工控机,装系统......不知不觉就10点了,回来想到可以像前几天做leetcode上Wildcard Matching那样动态规划来做。
这是个字符串问题。我们的字符串只包含大写字母。我们定义一个字母和它13位以后的字母互补。即A与N互补,B与O互补,C与P互补……,我们需要找一个字符串的两个不重叠的子串,其中一个和另外一个的翻转全互补。另外,因为我们的问题来自生物学,所以我们允许这两个字符串的一个可以删掉两个字符(注意:仅允许一个字符串删掉任意位置的两个字符,另外一个必须全部互补上)。我们的目的是求最长的反向互补子串的长度。
输入格式:
多组数据,每组数据一行,有一个字符串。每个字符串长度不超过2000,只有大写英文字母组成。
输出格式:
每组数据输出一行包含一个整数,表示最长的反向互补子串长度。
答题说明
输入样例
ABCD
ABCDQOPN
ABON
输出样例:
0
2
2
解释:
第一个样例,没有互补的子串。
第二个样例,AB和NO互补,所以反向互补子串的时候我们可以选择NPO,删掉一个P即可。
第三个样例,AB与ON反向互补。
动态规划
str为输入的字符串,str1为str的逆向存储字符串。
int getMax(int i,int j,int times);返回的是从str的i、str1的j开始,str的子串中还能删除(2-times)个字符(说明i前的子串已经删除了times个字符)的满足题意的反向互补字串的最大长度。那么:
(1)当s[i]+13==ss[j]||s[i]==ss[j]+13时:
如果 times <2
getMax(i,j,times)=max(getMax(i+1,j,times+1),1+getMax(i+1,j+1,times)); //是否不比较str的第i个字符,即在这个子串中删除这个字符
否则
getMax(i,j,times)=1+getMax(i+1,j+1,times);
(2)不满足上述等式时
如果 times <2
getMax(i,j,times)=getMax(i+1,j,times+1); //是否不比较str的第i个字符,即在这个子串中删除这个字符
否则
然后通过 ret1=max( getMax(i,j,0) ) , 0<= i,j<=s.size()-1; 得出只在前面的字符串中删除字符的最大反向互补字串的长度。
然后交换str与str1内容,ret2=max( getMax(i,j,0) ) , 0<= i,j<=s.size()-1; 得出只在后面的字符串中删除字符的最大反向互补字串的长度。
结果就是max(ret1,ret2)。
注意:根据对称性,只需计算正向子串在反向子串前面的情况,即i<s.size()-1-j的情况。
已经有不少人通过,没有奖品了,代码公布如下:
#include <iostream> #include <string> #include <map> #include <algorithm> std::string s; std::string ss; int getMax(int i,int j,int times){ if(i<s.size()-1-j){ if(s[i]+13==ss[j]||s[i]==ss[j]+13){ if(times<2) return std::max(getMax(i+1,j,times+1),1+getMax(i+1,j+1,times)); else return 1+getMax(i+1,j+1,times); }else{ if(times<2) return getMax(i+1,j,times+1); else return 0; } }else return 0; } int getMaxLen(){ int ret=0; if(ss.size()<2) return ret; for(int i=0;i<s.size();++i) for(int j=0;j<s.size();++j){ int temp=getMax(i,j,0); ret=temp>ret?temp:ret; } return ret; } int main(int argc,char** argv){ while(std::cin>>s){ ss.assign(s.rbegin(),s.rend()); int ret1=getMaxLen(); if(ret1==s.size()/2){ std::cout<<ret1<<std::endl; continue; } s.swap(ss); int ret2=getMaxLen(); std::cout<<(ret1>=ret2?ret1:ret2)<<std::endl; } return 0; }
#include <iostream> #include <string> #include <map> #include <algorithm> std::string s; std::string ss; const int maxLen=2000; int dp[maxLen][maxLen][3];//最后一位代表删除几个字符 int getMaxLen1(){ int ret=0; if(ss.size()<2) return ret; for(int k=2;k>=0;--k){ for(int i=s.size()-1;i>=0;--i){ for(int j=s.size()-1;j>=0;--j){ if(i>=s.size()-1-j){ dp[i][j][k]=0; continue; } if(s[i]+13==ss[j]||s[i]==ss[j]+13){ if(k<2) dp[i][j][k]=std::max(dp[i+1][j][k+1],1+dp[i+1][j+1][k]); else dp[i][j][k]=1+dp[i+1][j+1][k]; }else{ if(k<2) dp[i][j][k]=dp[i+1][j][k+1]; else dp[i][j][k]=0; } } } } //get the max for(int i=s.size()-1;i>=0;--i) for(int j=s.size()-1;j>=0;--j) ret=dp[i][j][0]>=ret?dp[i][j][0]:ret; return ret; } int main(int argc,char** argv){ while(std::cin>>s){ ss.assign(s.rbegin(),s.rend()); int ret1=getMaxLen1(); if(ret1==s.size()/2){ std::cout<<ret1<<std::endl; continue; } s.swap(ss); int ret2=getMaxLen1(); std::cout<<(ret1>=ret2?ret1:ret2)<<std::endl; } return 0; }