字符串+尺取法 - HDU6103

 

 

Kirinriki

 

 

 

题意:定义两个字符串之间的距离为: disA,B=∑i=0n−1|AiBn−1−i| , 给定一个字符串,选取两个不想交的子串,使得两个子串的距离为小于给定的m的最大值。

 

数据范围:

0≤m≤5000
Each character in the string is lowercase letter, 2≤|S|≤5000
∑|S|≤20000

 

思路:

可以采取定二分枚举长度,然后枚举起始位置,两个字符串同时向中间移动,这样每次就只要减去 距离较远的两端的一个字符的距离,加上距离较近的一个字符的距离,这样可以省去一重循环。枚举左边的起始位置是不能把子串枚举完的,所以需要再枚举一次右侧的起始位置。

时间复杂度为:O(n*n*logn)  1s内能解。

 

AC代码

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>

using namespace std;

const int ma=5000+100;
char s[ma];
int m,len;

bool solve(int x)
{
    int ans;
    for(int i=1; i<=len-2*x+1; ++i)  //枚举左端点
    {
        ans=0;
        for(int k=0; k<x; ++k)
            ans+=abs(s[i+k]-s[len-k]);
        if(ans<=m) return true;
        int ls=i,le=i+x-1;
        int rs=len-x+1,re=len;
        while(le+1<rs-1)   //两个子串同时向中间移动
        {
            ans=ans-abs(s[re]-s[ls]);
            ls++;le++;
            rs--;re--;
            ans=ans+abs(s[rs]-s[le]);
            if(ans<=m) return true;
        }
    }

    for(int i=len; i>=2*x; --i)   //枚举右区间
    {
        ans=0;
        for(int k=0; k<x; ++k)
            ans+=abs(s[i-k]-s[k+1]);
        if(ans<=m) return true;
        int ls=1,le=x;
        int rs=i-x+1,re=i;
        while(le+1<rs-1)
        {
            ans=ans-abs(s[re]-s[ls]);
            ls++;le++;
            rs--;re--;
            ans=ans+abs(s[rs]-s[le]);
            if(ans<=m) return true;
        }
    }
    return false;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&m);
        scanf("%s",s+1);
        len=strlen(s+1);

        int l=0,r=len/2+1,mid;
        while(r-l>1)    //二分枚举长度
        {
            mid=(l+r)/2;
            if(solve(mid)) l=mid;
            else r=mid;
        }
        printf("%d\n",(l+r)/2);
    }
    return 0;
}

#include <stdio.h>
#include <algorithm>

using namespace std;

const int ma=5000+100;
char s[ma];
int m,len;

bool solve(int x)
{
    int ans;
    for(int i=1; i<=len-2*x+1; ++i)  //枚举左端点
    {
        ans=0;
        for(int k=0; k<x; ++k)
            ans+=abs(s[i+k]-s[len-k]);
        if(ans<=m) return true;
        int ls=i,le=i+x-1;
        int rs=len-x+1,re=len;
        while(le+1<rs-1)   //两个子串同时向中间移动
        {
            ans=ans-abs(s[re]-s[ls]);
            ls++;le++;
            rs--;re--;
            ans=ans+abs(s[rs]-s[le]);
            if(ans<=m) return true;
        }
    }

    for(int i=len; i>=2*x; --i)   //枚举右区间
    {
        ans=0;
        for(int k=0; k<x; ++k)
            ans+=abs(s[i-k]-s[k+1]);
        if(ans<=m) return true;
        int ls=1,le=x;
        int rs=i-x+1,re=i;
        while(le+1<rs-1)
        {
            ans=ans-abs(s[re]-s[ls]);
            ls++;le++;
            rs--;re--;
            ans=ans+abs(s[rs]-s[le]);
            if(ans<=m) return true;
        }
    }
    return false;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&m);
        scanf("%s",s+1);
        len=strlen(s+1);

        int l=0,r=len/2+1,mid;
        while(r-l>1)    //二分枚举长度
        {
            mid=(l+r)/2;
            if(solve(mid)) l=mid;
            else r=mid;
        }
        printf("%d\n",(l+r)/2);
    }
    return 0;
}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章