csdn英雄会题解之第五届在线编程大赛月赛指定题目:反向互补子串

同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个字符,即在这个子串中删除这个字符
           否则               

                    getMax(i,j,times)=0;

然后通过 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;
}

可以通过map<vector>保存中间结果,但是没有保存中间计算结果居然也能通过。

优化-状态转移矩阵实现代码

#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;
}


你可能感兴趣的:(CSDN英雄会,csdn英雄会题解,月赛指定题目,反向互补子串,第五届在线编程大赛)