字符串+尺取法 - 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;
}

 

 

 

 

 

 

 

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