hdu 6103 Kirinriki(尺取法)

来源:互联网 发布:在哪购买淘宝小号靠谱 编辑:程序博客网 时间:2024/05/16 11:50

http://acm.hdu.edu.cn/showproblem.php?pid=6103
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|≤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
题目大意:给定一个整数m和一个字符串s,规定两个字符串的距离 这里写图片描述,在s的相同 长度子串中找出距离小于等于m的最长子串的长度。

解题思路:因为 题目上说明两个子串不重合,且子串的距离小于等于n,所以我们可以用尺取法枚举出子串所有的情况这样时间复杂度只是O(n^2),在所有子串中找出符合条件的子串,输出长度即可。
下面介绍一下尺取法:尺取法通常是指对数组保存一对下标(起点、终点),然后根据实际情况交替推进两个端点直到得出答案的方法,因为这种方法像尺取虫的爬行方式所以得名。其实就是求在一个线性的数组上求关于区间的问题。由于只对区间的两个端点进行改变,所以中间部分就不需要进行维护更新了,只要维护两端即可。可以大大降低复杂度。
尺取法的使用情况:
1 . 给定长度为n的整数数列以及整数S,求出总和不小于S的连续子序列的长度的最小值,如果解不存在,输出0。
2 . 给出一串珠子,有2n个,其中有黑色和白色珠子各n个,黑色逆时针编号,白色顺时针编号,白色黑色随机排列,要找一段长度为n的区间,要求区间内珠子的编号为1-n(不一定从小到大或从大到小出现,只要1-n都出现即可)。数据范围1000万
这个题是第一种情况的变形,
这里写图片描述
像这样枚举出所有对称点的取值可能即可。
CODE:

#include <bits/stdc++.h>using namespace std;int ans,n;void solve(char str[]){    for(int i=2;i<=strlen(str);i++){        int mid = i/2,sum = 0,l=0,len = 0;///sum保存两个串的距离,len保存子串的长度,l保存A串的起始位置,mid表示B串的起始位置        for(int j = 0; j < mid; j++){            sum += abs(str[j]-str[i-j-1]);            if(sum<=n)///满足条件时子串长度加一,并更新ans的值            {                len++;                ans = max(ans,len);            }            else{                sum -= abs(str[l]-str[i-l-1]);///不满足条件时,将首尾的两个字符串去掉                sum -= abs(str[j] -str[i-j-1]);///将当前新加上的也去掉                len--;                l++;                j--;///因为当前新加上的去掉了,所以相应的j也应该减一            }        }    }}int main(){    int T;scanf("%d",&T);    while(T--){        ans = 0;        char str[5005];        scanf("%d %s",&n,str);        int len = strlen(str);        solve(str);        reverse(str,str+len);        solve(str);        cout<<ans<<endl;    }     return 0;}