2017 多校赛6 Kirinriki hdu 6103

飞机直达

题目

题目意思:题意还是不难的吧,虽然我一开始没看懂non-overlapping是不覆盖的意思。。。还是说一下吧, 给定一个串S, 要找两个最长的不相互覆盖且长度相等的子串s1,s2,使得把其中一个子串倒转后与另一子串的距离不大于m(感觉有点类似回文串的东西)。。
思路:一开始我没注意道两个串不可相互覆盖, 想了一个dp,状态转移很奇怪。 后来发现看错题目却来了思路Orz。。。子串长度相等,不可相互覆盖,那么他们一定是 “对称” 的,这个对称是指在忽略一定距离误差后轴对称。那么枚举中心就是一个很自然的思路了。那么当中心确定之后,要怎么求最长的子串呢?让我们只考虑中心右边的子串s2,我们知道左边字符可以通过右边字符下标和中间位置直接求得(即s2的dis可得), 这样问题就转化成了:求一个最长子串s2,dis不大于m,这里dis已经可以看成是s2每个字符的cost了。说到这里,思路差不多就出来了吧。。。是的,尺取法!!!
赛后想了一下,似乎串可以相互覆盖也可以用相同的思路来做-_-.

#include <cstring>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long LL;

#define MAXN 6
int const maxn=5100;
char str[maxn];
int m;
inline int dis(char a, char b)
{
    return a>b? a-b : b-a;
}
int main(void){
    int T;
    for (scanf("%d", &T); T; T--){
        scanf("%d", &m);
        scanf("%s", str+1);
        int ans=0, len=strlen(str+1);
        for (int mi=1; mi<=len; mi++){//枚举中点,mi作为中点。s1,s2都不取str[mi]
            int s=mi+1, t=mi+1, sum=0;
            for (;;){
                while (t<=len && mi*2-t>0 && sum<=m){
                    sum+=dis(str[t], str[mi*2-t]); t++;
                    if(sum<=m) ans= max(ans, t-s);//这个可省不得,挑战的模板应该是有问题
                }
                if(sum<=m) ans= max(ans, t-s);
                if (t>len || mi*2-t<=0) break;
                sum-=dis(str[s], str[mi*2-s]);
                s++;
            }
        }
        for (int mi=1; mi<=len; mi++){//枚举中点,mi与mi+1的中间的空位作为中心轴。mi作为s2的第一个字符下标,不属于s1.
            int s=mi, t=mi, sum=0;
            for (;;){
                while (t<=len && mi*2-t-1>0 && sum<=m){
                    sum+=dis(str[t], str[mi*2-t-1]); t++;
                    if(sum<=m) ans= max(ans, t-s);//同样不能省
                }
                if(sum<=m) ans= max(ans, t-s);
                if (t>len || mi*2-t-1<=0) break;
                sum-=dis(str[s], str[mi*2-s-1]);
                s++;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

这道题是个想法+编程技巧题,感觉算是与水题拉开的第一个有区分度的题吧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值