HDU 6103 Kirinriki (尺取)
Description
定义两个长度同为\(n\)的字符串A和B之间的距离为
\[dis(A,B)=\sum_{i=0}^{n-1}{A_i-B_{n-1-i}}
\]
给出一个字符串S,请你求出S的两个长度不超过\(m\)且不重叠子串的最大长度。
Input
第一行给出用例组数\(T\),对于每组测试用例,第一行给出\(m\),第二行给出字符串S。\(T \leqslant 100\),\(0 \leqslant m \leqslant 5000\),\(2 \leqslant |S| \leqslant 5000\)。
Output
对于每组用例,输出一个整数,表示字串的最大长度。
Sample Input
1
5
abcdefedcb
Sample Output
5
Solution
简单分析可知,最终答案的形式一定是A串和B串之间有一条对称轴,从对称轴向两边延伸,A串和B串中对应位置字符的差的绝对值求和不超过\(m\),而此时的A,B串长度最长。
对称轴有两种,一种以字符为对称轴,一种以字符之间的间隔为对称轴,分别枚举这两种对称轴。一旦对称轴确定,两侧对应字符对也随之确定,此时问题转化为给出一段序列,求和不大于\(m\)的最长序列的长度。典型的尺取问题。
给出上限限制k,依次推进左端点,对于每个左端点,向右移动右端点直到第一个不合法的位置,此时区间长度减一即为当前左端点对应的答案。随着左端点的推进,会出现右端点直到最右端也不会不合法的情况,此时的区间长度就是当前左端点的答案。因为此后区间不会更长,更新后跳出即可。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 5e3 + 10;
char s[N];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int m;
scanf("%d%s", &m, s);
int len = strlen(s);
int ans = 0;
for (int mid = 1; mid <= len - 2; mid++)
{
int lb = mid + 1;
int ub = lb + min(mid, len - mid - 1);
for (int l = lb, r = lb, sum = 0; l < ub; l++)
{
while (r < ub && sum <= m) sum += abs(s[r] - s[2 * mid - r]), r++;
if (sum <= m) { ans = max(ans, r - l); break; }
ans = max(ans, r - l - 1);
sum -= abs(s[l] - s[2 * mid - l]);
}
}
for (int mid = 1; mid <= len - 1; mid++)
{
int lb = mid;
int ub = lb + min(mid, len - mid);
for (int l = lb, r = lb, sum = 0; l < ub; l++)
{
while (r < ub && sum <= m) sum += abs(s[r] - s[2 * mid - 1 - r]), r++;
if (sum <= m) { ans = max(ans, r - l); break; }
ans = max(ans, r - l - 1);
sum -= abs(s[l] - s[2 * mid - 1 - l]);
}
}
printf("%d\n", ans);
}
return 0;
}