HDU 6103 2017ACM暑期多校联合训练 - Team 6 1008 Kirinriki
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.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|≤20000Output
For each test case output one interge denotes the answer : the maximum length of the substring.Sample Input
1 5 abcdefedcbSample 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题意:
给定一个字符串与一个距离dis的最大限制,在该串中找到两个等长的无交叉的子串,求在满足dis限制下的最长的子串的长度。
dis的定义为:disA,B=∑i=0n−1|Ai−Bn−1−i|,A、B分别从给定字符串中截取的等长度的无交叉的子串。
分析:
最原来以为要一个一个的循环遍历,这样的话时间复杂度太大,中间也有很多的重复的部分,考虑到这些因素的话,用尺取法来考虑这个问题。
首先我们可以假设 A 一定在 B 左边,然后我们可以考虑A的起点和B的尾部,如果暂时不考虑长度不固定,我们每次查找都让长度尽可能长,那么,我们一定需要将 A的起点和B的尾部,然后获取 A 向右延伸,B 向左延伸对应位置的贡献,然后呢?我们可以采用尺取法来进行A的起点和B的尾部向中间的推移,当 此时的dis值大于 m 时,我们向中间推移 A的起点和B的尾部即可。在这个尺取法的过程中,记录下来最大的 dis 值即可。
简单的说就是,将整个区间划分为两份,然后左右各有一个序列,这个序列是贪心的,贪心的过程利用尺取法来维护最优解,而这里我们需要对区间的划分进行枚举。
这样的话考虑到一种情况就是每次的时候,我们都是在用这个字符串的后半部分来匹配前半部分的任意一种情况,相当于后半部分的字符串是基本上固定的,这样的话匹配的可能行机会减少,为了解决设个问题,我们只需要将字符串逆序,再按照你这的字符串同样的方法比较一下,找出其中的最大值即可。
#includeusing namespace std;const int maxn=21000;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(){ int T; scanf("%d",&T); while(T--) { int ans=0; scanf("%d%s",&m,str+1);///从第一位开始赋值 len=strlen(str+1); ans=max(ans,solve());///正序的这次是拿后面的那一部分来与前面的那一部分进行匹配 reverse(str+1,str+len+1); ans=max(ans,solve());///反序的正好反过来,是用原序列里面前面的那一部分来匹配后面的那一部分 printf("%d\n",ans); } return 0;}