ACM算法:尺取法

尺取法的用途:在大多数情况下,用于解决给定序列中,搜寻某个符合特定要求的子序列,比如一串数字序列,找

到“连续且和为某值的最大(小)子序列”“连续且不存在重复数字的最大子序列”等等,这可以说是一系列问题了。如果用

在字符串上,可能你会联想到KMP或者BF这种模式匹配的算法,其实他们有共同点。


尺取法的思想:顾名思义,像使用一把尺子一样,你用这个算法按照某个规则“量”出子序列。所以,你需要一个起点(front)

和一个终点(rear)用来测量子序列。让后你通过不断的移动front和rear,进而找到你所需要的子序列(这可比枚举效率高多了)。


说到这你可能也意识到了尺取法适合用于有一定规律的序列中,或者说是连续的序列中,当然,算法并不是死板的,只要可以,

你想用在哪都没问题。


例题解析:

毕竟不管做什么,学习思想之后,通过练习才能融会贯通。这里我选了一道比较经典的题,用两种方式(一种用循环,一种调

用STL算法)实现尺取法。


实例:Unique Snowflakes


Emily the entrepreneur has a cool businessidea: packaging and selling snowflakes. She has devised a machine that capturessnowflakes as they fall, and serializes them into a stream of snowflakes thatflow, one by one, into a package. Once the package is full, it is closed andshipped to be sold. The marketing motto for the company is “bags ofuniqueness.” To live up to the motto, every snowflake in a package must bedifferent from the others. Unfortunately, this is easier said than done,because in reality, many of the snowflakes flowing through the machine areidentical. Emily would like to know the size of the largest possible package ofunique snowflakes that can be created. The machine can start filling thepackage at any time, but once it starts, all snowflakes flowing from themachine must go into the package until the package is completed and sealed. Thepackage can be completed and sealed before all of the snowflakes have flowedout of the machine. Input The first line of input contains one integer specifyingthe number of test cases to follow. Each test case begins with a linecontaining an integer n, the number of snowflakes processed by the machine. Thefollowing n lines each contain an integer (in the range 0 to 109 , inclusive)uniquely identifying a snowflake. Two snowflakes are identified by the sameinteger if and only if they are identical. The input will contain no more thanone million total snowflakes.

 Output

For each test case output a line containingsingle integer, the maximum number of unique snowflakes that can be in apackage.

 Sample Input

1

5

1

2

3

2

1

SampleOutput

3


题目的大意:给数字序列,找到其中连续且不存在相等值的最大长度子序列。例题的序列是1、2、3、2、1。

#include 
#include 
#include 
using namespace std;

int ves[1000010];

int main(void)
{
	ios::sync_with_stdio(false);
	int T;
	cin >> T;	//输入案例数
	while (T--) {
		memset(ves, 0, sizeof(ves));
		int M;
		cin >> M;	//输入序列共有多少个值
		for (int i = 0; i < M; i++) {
			cin >> ves[i];
		}

		int front, rear;//rear指向子序列的末尾的下一个位置
		front = 0; rear = 1;
		int count = rear - front; int Max = 1;
		while (rear != M) {
			int ptr = front;//ptr用于在子序列中遍历的操作
			while (ptr != rear) {
				if (ves[ptr] != ves[rear]) ptr++;
				else {
					count = rear - ptr;		//count用于计算每次遇见相同值时,目前子序列的最大长度
					front = ptr + 1;
					break;
				}
			}
			if (ptr == rear) count++;
			rear++;
			Max = max(count, Max);
		}

		cout << Max << endl;
	}

	return 0;
}

这种方式超时,但是思路比较清晰。运行中的模拟(有点粗糙)

(1) (2) (3)
front:0 0 0 2
rear:1 2 3 4
Max:1 2 3 3
count:1 2 3 2
序列:1 2 3 2 1


#include
#include
#include
using namespace std;
const int N = 1000005;
int a[N];
int maxi(int a, int b)
{
	return a>b ? a : b;
}
int main()
{
	ios::sync_with_stdio(false);

	int t, n;
	set s;
	cin >> t;
	while (t--)
	{
		s.clear();
		cin >> n;
		for (int i = 1; i <= n; i++)
			cin >> a[i];

		int left = 1, right = 1, num = 0;

		while (right <= n)
		{
			while (right <= n && !s.count(a[right]))
				s.insert(a[right++]);
			num = maxi(num, right - left);
			s.erase(a[left++]);
		}
		cout << num << endl;
	}
	return 0;
}
这种方式是可以AC的,思路和上面的一致。

总结:

尺取法的思想并不难,效率比一般枚举高得多,而且还是适用于一类的问题,所以需要我们去掌握。当然检验掌握与否就是要靠

做题,你不去做题,就算知道思想,到时候也未必能实现。

你可能感兴趣的:(尺取法,ACM,Unique,Snowflakes,算法,ACM算法)