题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6103
二分。
首先预处理一个dp[i][j],表示s[i~j]这连续的一段,从中间分开,两个子串的距离。
二分一个长度,判断是否存在两个长度为mid的子串距离不大于m。
判断的方法是:枚举两个子串的起点 i 和 j,那么两个长度为mid的子串的距离就是 dp[i, j+mid-1] - dp[i+mid][j-1]。
会卡空间,dp数组要用unsigned short,int会超内存,short会WA。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 5000 + 10;
char S[maxn];
unsigned short dp[maxn][maxn];
int len, m;
bool check(int l){
for (int i=0; i<len; i++){
for (int j=i+l; j<len; j++){
if (j+l-1 >= len) break;
if (dp[i][j+l-1] - dp[i+l][j-1] <= m){
return true;
}
}
}
return false;
}
int main()
{
int t;
scanf("%d", &t);
while (t > 0){
t--;
scanf("%d", &m);
scanf("%s", S);
getchar();
len = strlen(S);
for (int l=1; l<=len; l++){
for (int i=0; i<len; i++){
int j = i+l-1;
if (j >= len) continue;
if (i == j) dp[i][j] = 0;
else if (i == j-1) dp[i][j] = abs(S[i]-S[j]);
else dp[i][j] = dp[i+1][j-1] + abs(S[i]-S[j]);
}
}
int l = 1;
int r = len / 2;
int ans = 0;
while (l <= r){
int m = (l + r ) >> 1;
if (check(m)){
ans = m;
l = m + 1;
}else{
r = m - 1;
}
}
cout << ans << endl;
}
return 0;
}