HDU 6103 Kirinriki 枚举长度 尺取法

  题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6103

  题目描述: 定义两个相同长度的字符串的值为首尾开始字符串的ASCII相减, 求一个字符串中任取两个相同长度的不重叠的值不超过m的最大长度

  解题思路: 求连续区间不超过某一个上限或者不低于某个下限的应该用尺取法 ,复杂度为O(n),  本题n是5000所以O(n^2)可行, 枚举前缀和后缀(通过枚举前缀, 再将字符串翻转枚举前缀进行), 再进行尺取 

  代码:  代码中有注释

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <iterator>
#include <cmath>
#include <algorithm>
#include <stack>
#include <deque>

#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;

const int maxn = 21000;
char str[maxn];
int m,len;

int solve() {
    int ans = 0;
//    cout << len << endl;
    for( int i = len; i >= 1; i-- ) { // 枚举每一个前缀
//        cout << "--- " << endl;
        int cnt = i/2-1;
        int  d = 0,t = 0,p = 0;
        for( int j = 0 ; j <= cnt; j++ ) {
            d += abs( str[1+j]-str[i-j] ); // 加上权值
            if( d > m ) {
                d -= abs( str[1+p]-str[i-p] ); // 将最前面的剪掉, 将最后面剪掉, 这样下一次循环还会加的是最后面
                d -= abs( str[1+j]-str[i-j] );
                p++; // 记录第一个字符串最前面的指针
                t--; // 记录单个字符串的长度
                j--; // 记录第一个字符串最后面的指针
            }
            else {
                t++; // d <= m 就是说还可以向后再加一个字符串长度可以加一
                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 = (int)strlen(str+1);
        ans = max( ans, solve() );
        reverse( str+1, str+len+1 ); // 后缀和
        ans = max( ans, solve() );
//        reverse( str+1, str+len+1 );
        printf("%d\n",ans);
    }
    return 0;
}
View Code

  思考: 以前看过尺取法, 忘了......以后记住求连续区间不超过某一个上限或者不低于某个下限的长度应该用尺取法 

posted on 2017-08-11 09:49  FriskyPuppy  阅读(209)  评论(0编辑  收藏  举报

导航