尺取法

Posted joker-wz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尺取法相关的知识,希望对你有一定的参考价值。

最近在做一道算法提示,遇到了使用尺取法,所以就来总结一下尺取法和那道算法题。

尺取法的常用做法是保存数组的一对下标,分别称为起点和终点,然后始终保持起点和终点间的数据串是符合要求的。之后根据需求交替推进两个端点,直至得到答案。这种方式有些类似毛毛虫的前进。其实就是求在一个线性的数组上求关于区间的问题。由于只对区间的两个端点进行改变,所以中间部分就不需要进行维护更新了,只要维护两端即可。可以大大降低复杂度。

题目描述

有一条彩色宝石项链,是由很多种不同的宝石组成的,包括红宝石,蓝宝石,钻石,翡翠,珍珠等。有一天国王把项链赏赐给了一个学者,并跟他说,你可以带走这条项链,但是王后很喜欢红宝石,蓝宝石,紫水晶,翡翠和钻石这五种,我要你从项链中截取连续的一小段还给我,这一段中必须包含所有的这五种宝石,剩下的部分你可以带走。如果无法找到则一个也无法带走。请帮助学者找出如何切分项链才能够拿到最多的宝石。

输入描述

我们用每种字符代表一种宝石,A表示红宝石,B表示蓝宝石,C代表紫水晶,D代表翡翠,E代表钻石,F代表玉石,G代表玻璃等等,我们用一个全部为大写字母的字符序列表示项链的宝石序列,注意项链是首尾相接的。每行代表一种情况。

输出描述

输出学者能够拿到的最多的宝石数量。每行一个

输入

ABCYDYE
ATTMBQECPD

输出

1
3

本题关键就是要找出包含A, B, C, D, E的最小子串,需要注意的是输入的串是一个环,表明任意一个字符都可以是起点,所以我们将字符串拷贝一遍然后拼接到一起这样就可以用一个数组大概的表示出环。在拷贝环之前我们需要备份之前字符串的长度,用这个长度的值表示初始最小的字串。现在可以选取起点终点都为0处,然后终点一步一步右移,在每次移动是判断当前包含是否符合题目要求,直至满足要求为止。更新新最小子串的长度。然后开始右移起点,每右移一次检测一次是否合法,若合法继续右移,若不合法终止起点的右移,开始右移终点使之再次符合要求,并再次判断是否更新最小子串。重复下去直至终点移到字符串最后还没有满足条件的最小字串。

#include <iostream>
#include <string>
#include <algorithm>
#include <map>
using namespace std;

int main()
{
    string str;
    while (cin >> str)
    {
        int len = str.length(), Min, i = 0, j = 0, num = 0;
        map<char, int> m;//记录ABCDE出现的次数
        Min = len;    //包含ABCDE的最小长度
        str += str;    //相当于构造一个环
        while (true)
        {

            while (i < str.length() && num < 5)
            {//右端点右移
                if ((str[i] == A || str[i] == B || str[i] == C || str[i] == D || str[i] == E) && m[str[i]]++ == 0)
                    num++;
                i++;
            }
            if (num < 5) break;
            Min = min(Min,i-j);
            //左端点开始右移
            if ((str[j] == A || str[j] == B || str[j] == C || str[j] == D || str[j] == E) && --m[str[j]] == 0)
                num--;
            j++;
        }
        cout << len - Min << endl;
    }
    return 0;
}

 



以上是关于尺取法的主要内容,如果未能解决你的问题,请参考以下文章

尺取法 poj 2566

poj3320(尺取法)

poj2739(尺取法+质数筛)

51nod1127(尺取法)

Codeforces 1156C 尺取法 / 二分

poj3061(尺取法)