Kirinriki
题意:定义两个字符串之间的距离为: disA,B=∑i=0n−1|Ai−Bn−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;
}