题目描述
给定一个字符串,问最大字串长度为多少的时候交叉相减的绝对值之和小于等于输入的m,注意,子串不能重合。
解题思路
最原来以为要一个一个的循环遍历,这样的话时间复杂度太大,中间也有很多的重复的部分,考虑到这些因素的话,用尺取法来考虑这个问题。
首先我们可以假设 A 一定在 B 左边,然后我们可以考虑A的起点和B的尾部,如果暂时不考虑长度不固定,我们每次查找都让长度尽可能长,那么,我们一定需要将 A的起点和B的尾部,然后获取 A 向右延伸,B 向左延伸对应位置的贡献,然后呢?我们可以采用尺取法来进行A的起点和B的尾部向中间的推移,当 此时的dis值大于 m 时,我们向中间推移 A的起点和B的尾部即可。在这个尺取法的过程中,记录下来最大的 dis 值即可。
简单的说就是,将整个区间划分为两份,然后左右各有一个序列,这个序列是贪心的,贪心的过程利用尺取法来维护最优解,而这里我们需要对区间的划分进行枚举。
这样的话考虑到一种情况就是每次的时候,我们都是在用这个字符串的后半部分来匹配前半部分的任意一种情况,相当于后半部分的字符串是基本上固定的,这样的话匹配的可能行机会减少,为了解决设个问题,我们只需要将字符串逆序,再按照你这的字符串同样的方法比较一下,找出其中的最大值即可。
转载自我亲爱的小学妹:博客链接:http://blog.csdn.net/qq_37412229/article/details/77073094
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <string>
#include <algorithm>
using namespace std;
const int maxn = 5010;
string str;
int solve(int m,int len)
{
int ans = 0;
for(int j = len-1; j >= 0; j--) ///枚举B字符串的终点
{
int End; ///求A字符串的终点
if(j%2==0) End = j/2-1;
else End = j/2;
int dis,length,num; ///dis当前字符串的距离,length当前字符串的额长度,num是A,B头尾去除的字符个数。
dis = length = num = 0;
for(int i = 0; i <= End; i++)
{
dis += abs(str[i]-str[j-i]);
if(dis > m) ///去掉A的开头和B的结尾
{
dis -= abs(str[num] - str[j-num]);
num++;
}
else
{
length++;
ans = max(ans,length);
}
}
}
return ans;
}
int main()
{
int t,m;
cin>>t;
while(t--)
{
cin>>m>>str;
int len = str.length();
int ans = solve(m,len);
reverse(str.begin(),str.end()); ///反转字符串再求一次。
ans = max(ans,solve(m,len));
printf("%d\n",ans);
}
return 0;
}