HDU 6103 二分求最大子串

本文探讨了一种特殊的字符串距离计算方法,并通过两种高效的算法实现了解决方案的优化。问题要求寻找两个非重叠子串的最大长度,使得它们之间的特定距离不超过给定阈值。文章提供了两种算法思路:一种利用预处理和二分查找,另一种采用动态滑窗技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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=0n1|AiBn1i|
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
T100
0m5000
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
  
5
Hint
[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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值