题目描述
给定一个字符串,问最大字串长度为多少的时候交叉相减的绝对值之和小于等于输入的m,注意,子串不能重合。
解题思路
最原来以为要一个一个的循环遍历,这样的话时间复杂度太大,中间也有很多的重复的部分,考虑到这些因素的话,用尺取法来考虑这个问题。
首先我们可以假设 A 一定在 B 左边,然后我们可以考虑A的起点和B的尾部,如果暂时不考虑长度不固定,我们每次查找都让长度尽可能长,那么,我们一定需要将 A的起点和B的尾部,然后获取 A 向右延伸,B 向左延伸对应位置的贡献,然后呢?我们可以采用尺取法来进行A的起点和B的尾部向中间的推移,当 此时的dis值大于 m 时,我们向中间推移 A的起点和B的尾部即可。在这个尺取法的过程中,记录下来最大的 dis 值即可。
简单的说就是,将整个区间划分为两份,然后左右各有一个序列,这个序列是贪心的,贪心的过程利用尺取法来维护最优解,而这里我们需要对区间的划分进行枚举。
这样的话考虑到一种情况就是每次的时候,我们都是在用这个字符串的后半部分来匹配前半部分的任意一种情况,相当于后半部分的字符串是基本上固定的,这样的话匹配的可能行机会减少,为了解决设个问题,我们只需要将字符串逆序,再按照你这的字符串同样的方法比较一下,找出其中的最大值即可。
代码部分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 10;;
char str[maxn];
int m, len;
int solve()
{
int ans = 0;
for(int i = len; i >= 1; -- i)///B子串的结尾处
{
int cnt = i / 2 - 1, d = 0, t = 0, p = 0;///cnt表示的是A子串的结尾处,d表示的是当前串的dis,t表示的是串的长度
for(int j = 0; j <= cnt; ++ j)///j表示的是A串的开始坐标
{
d += abs(str[1 + j] - str[i - j]);///串的长度往后扩展
if(d > m)///这里表示的是当前串的dis超过限制,将串从前面减少,然后从后面增加
{
d -= abs(str[1 + p] - str[i - p]);
d -= abs(str[1 + j] - str[i - j]);
p ++;///前面减去的串的长度
t --;///串的长度也要变化
j --;
}
else
{
t++;
ans = max(ans, t);
}
}
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while(t --)
{
int ans=0;
cin >> m >> str + 1 ;///从第一位开始赋值
len = strlen(str + 1);
ans = max(ans, solve());///正序的这次是拿后面的那一部分来与前面的那一部分进行匹配
reverse(str + 1, str + len + 1);
ans = max(ans, solve());///反序的正好反过来,是用原序列里面前面的那一部分来匹配后面的那一部分
cout << ans << endl;
}
return 0;
}
“`