Kirinriki
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 502 Accepted Submission(s): 183
Problem Description
We define the distance of two strings A and B with same length n is
disA,B=∑i=0n−1|Ai−Bn−1−i|
The difference between the two characters is defined as the difference in ASCII.
You should find the maximum length of two non-overlapping substrings in given string S, and the distance between them are less then or equal to m.
disA,B=∑i=0n−1|Ai−Bn−1−i|
The difference between the two characters is defined as the difference in ASCII.
You should find the maximum length of two non-overlapping substrings in given string S, and the distance between them are less then or equal to m.
Input
The first line of the input gives the number of test cases T; T test cases follow.
Each case begins with one line with one integers m : the limit distance of substring.
Then a string S follow.
Limits
T≤100
0≤m≤5000
Each character in the string is lowercase letter, 2≤|S|≤5000
∑|S|≤20000
Each case begins with one line with one integers m : the limit distance of substring.
Then a string S follow.
Limits
T≤100
0≤m≤5000
Each character in the string is lowercase letter, 2≤|S|≤5000
∑|S|≤20000
Output
For each test case output one interge denotes the answer : the maximum length of the substring.
Sample Input
1 5 abcdefedcb
Sample Output
5Hint[0, 4] abcde [5, 9] fedcb The distance between them is abs('a' - 'b') + abs('b' - 'c') + abs('c' - 'd') + abs('d' - 'e') + abs('e' - 'f') = 5
Source
题意:输入一个 m ,再输入一个字符串,要你找出两个最长的串(长度相等为len),str1和str2,这两个串的值就是
abs(str1[1] - str2[len]) + abs(str1[2] - str2[len - 1]) +...+ 就是两个串反过来后对应位置差值绝对值的和,要求你的和小于等于 m,问你最长为多少。
题目的数据为5000,一般的暴力为n^3甚至更多,根本过不去。
这道题有两个思路:
1:先处理出每个串的价值,这里用val[5000][5000]来存,而且 val[i][j] 指的是下标 i 到 j 这串从中间均分成两串后,这两串的对应位置差值绝对值的和。然后二分答案就可以了。
#include<stdio.h>
#include<string.h>
#include<math.h>
#define MAXN 5001
#define ll long long
char str[MAXN];
int m,len;
int val[MAXN][MAXN];
bool check(int x){
for(int i = 1;i <= len;i++){
if(i + 2 * x - 1 > len)
break;
for(int j = i + x;j <= len;j++){
if(j + x - 1 > len)
break;
if(val[i][j + x - 1] - val[i + x][j - 1] <= m)
return true; // 刨去中间多余的部分
}
}
return false;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&m);
scanf("%s",str + 1);
len = strlen(str + 1);
for(int i = 1;i <= len;i++){
for(int j = 1;j <= len;j++){
int k = i + j - 1;
if(k > len)
break;
if(j == k)
val[j][k] = 0;
else if(k - j == 1)
val[j][k] = abs(str[j] - str[k]);
else
val[j][k] = val[j + 1][k - 1] + abs(str[j] - str[k]);
}
} //先处理出每个串的值 复杂度为 n^2
int l = 1,r = len / 2,mid,ans;
while(l <= r){
mid = (l + r) / 2;
if(check(mid)){
ans = mid;
l = mid + 1;
}else{
r = mid - 1;
}
}
printf("%d\n",ans);
}
return 0;
}
2:枚举每个位置,然后从每个位置向两边展开,一遍算值,和小于等于m就继续展开,一旦大于了m,那么区间整体移动,再去看是否小于等于m,一直到边界位置,一遍记录最大的长度就可以了,这个方法时间短,所用空间也小,不需要辅助数组,个人认为这个算法比较好。要注意的是,枚举的那个点的左右两边剩余长的为奇数和偶数的时候会漏情况,所以要注意开始的位置
#include<bits/stdc++.h>
using namespace std;
#define MAXN 5005
int tmp[MAXN];
char str[MAXN];
int t,m,ans;
void solve(int len){
int j = 0,tot = 0;
for(int i = 0;i < len;i++){
while(j < len && tot + tmp[j] <= m){
tot += tmp[j++];
}
ans = max(ans,j - i);
if(i == j){
j++;
}else{
tot -= tmp[i];
}
}
}
int main(){
scanf("%d",&t);
int ppap[2] = {0,1};
while(t--){
scanf("%d",&m);
scanf("%s",str + 1);
int len = strlen(str + 1);
ans = 0;
for(int k = 0;k <= 1;k++){
for(int i = 1;i <= len;i++){
int k1 = i - 1,k2 = i + ppap[k];
int id = 0;
while(k1 >= 1 && k2 <= len){
tmp[id++] = abs(str[k1--] - str[k2++]);
}
solve(id);
}
}
printf("%d\n",ans);
}
return 0;
}