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