牛客编程题:彩色宝石项链

彩色宝石项链

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

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

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

示例
输入: ABCYDYE 输出: 1
输入: ATTMBQECPD 输出: 3

思路分析:穷举暴力法,利用两个指针,将项链进行切割,两个指针中需要包含ABCDE这五种宝石,项链总长度减去这段序列即未能获得的最大宝石数。
实例1的算法思路图解
在这里插入图片描述在这里插入图片描述

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int main() {
	string inputStr;
	while (cin >> inputStr) {
		int strLength = inputStr.size();
		int maxGet = 0;//能取得的最大宝石数
		//输入的宝石顺序的第一个开始寻找一段序列,并且逐渐移动指针
		for (int beginPtr = 0; beginPtr < strLength; ++beginPtr) {
			int flags[5] = { 0 };//由于标记五种宝石是否找到
			int endPtr = beginPtr;//尾指针,
			int cnt = 0;//选中的宝石序列中宝石个数
			int sumFlag = 0;//序列中已经找到的特殊宝石总数
			while (sumFlag < 5 && cnt < strLength) {
				//序列中已找到的特殊宝石总数小于5,且序列中的宝石数小于宝石总数
				if (endPtr < strLength) {
					//**因为初始宝石链是环状**,所以尾指针可能移到头指针的前面
					if (inputStr[endPtr] >= 'A' && inputStr[endPtr] <= 'E' && flags[inputStr[endPtr] - 'A'] == 0) {
						//如果尾指针指向的宝石(即将添加的宝石)是特殊宝石,且该宝石之前没有遇到
						flags[inputStr[endPtr] - 'A'] = 1;//标记已找到
						++sumFlag;//找到的特殊宝石总数自增
					}
					++cnt;//不管有没有找到,宝石序列都增加了一个长度
					++endPtr;//尾指针后移
				}
				else {
					//如果尾指针移动了头指针的前面,将序号对strLength求余即可
					if (inputStr[endPtr % strLength] >= 'A' && inputStr[endPtr % strLength] <= 'E' && flags[inputStr[endPtr % strLength] - 'A'] == 0) {
						//如果尾指针指向的宝石(即将添加的宝石)是特殊宝石,且该宝石之前没有遇到
						flags[inputStr[endPtr % strLength] - 'A'] = 1;//标记已找到
						++sumFlag;//找到的特殊宝石总数自增
					}
					++cnt;//不管有没有找到,宝石序列都增加了一个长度
					++endPtr;//尾指针后移
				}
			}
			if (maxGet < strLength - cnt) {
				//如果上一次寻找能获得的最大宝石数小于此次获得的,则更新
				maxGet = strLength - cnt;
			}
		}
		//输出结果
		cout << maxGet << endl;
	}
	return 0;
}

上面的代码通过了牛客的测试。

恭喜你通过本题
运行时间:4ms
占用内存:480k

对算法进行优化:注意到,上面的代码每次头指针向后移动后都是直接放入中间截出的序列中,并没有对头指针指向的宝石种类进行判断。不难发现,如果头指针指向的宝石不是特殊宝石,那么将头指针指向的宝石从中间截出的序列段中移去,操作后的截出序列任然满足条件,从而达到了缩减序列长度的效果,继而减少了寻找次数。优化思路,增加对头指针指向的宝石类型进行判断的功能。

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int main() {
	string inputStr;
	while (cin >> inputStr) {
		int strLength = inputStr.size();
		int maxGet = 0;//能取得的最大宝石数
		//输入的宝石顺序的第一个开始寻找一段序列,并且逐渐移动指针
		for (int beginPtr = 0; beginPtr < strLength; ++beginPtr) {
			if (inputStr[beginPtr] >= 'A' && inputStr[beginPtr] <= 'E') {
				//如果头指针指向的宝石类型是特殊类型才进行寻找操作
				int flags[5] = { 0 };//由于标记五种宝石是否找到
				int endPtr = beginPtr;//尾指针,
				int cnt = 0;//选中的宝石序列中宝石个数
				int sumFlag = 0;//序列中已经找到的特殊宝石总数
				while (sumFlag < 5 && cnt < strLength) {
					//序列中已找到的特殊宝石总数小于5,且序列中的宝石数小于宝石总数
					if (endPtr < strLength) {
						//因为初始宝石链是链状,所以尾指针可能移到头指针的前面
						if (inputStr[endPtr] >= 'A' && inputStr[endPtr] <= 'E' && flags[inputStr[endPtr] - 'A'] == 0) {
							//如果尾指针指向的宝石(即将添加的宝石)是特殊宝石,且该宝石之前没有遇到
							flags[inputStr[endPtr] - 'A'] = 1;//标记已找到
							++sumFlag;//找到的特殊宝石总数自增
						}
						++cnt;//不管有没有找到,宝石序列都增加了一个长度
						++endPtr;//尾指针后移
					}
					else {
						//如果尾指针移动了头指针的前面,将序号对strLength求余即可
						if (inputStr[endPtr % strLength] >= 'A' && inputStr[endPtr % strLength] <= 'E' && flags[inputStr[endPtr % strLength] - 'A'] == 0) {
							//如果尾指针指向的宝石(即将添加的宝石)是特殊宝石,且该宝石之前没有遇到
							flags[inputStr[endPtr % strLength] - 'A'] = 1;//标记已找到
							++sumFlag;//找到的特殊宝石总数自增
						}
						++cnt;//不管有没有找到,宝石序列都增加了一个长度
						++endPtr;//尾指针后移
					}
				}
				if (maxGet < strLength - cnt) {
					//如果上一次寻找能获得的最大宝石数小于此次获得的,则更新
					maxGet = strLength - cnt;
				}
			}
		}
		cout << maxGet << endl;
	}
	return 0;
}

恭喜你通过本题
运行时间:3ms
占用内存:457k

代码简化思路:不难发现,中间的while循环中,对endPtr讨论分成了两种情况,但显得有点多余,可以进行合并。因为小于strLength的数模上strLength后对本身没有影响。

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int main() {
	string inputStr;
	while (cin >> inputStr) {
		int strLength = inputStr.size();
		int maxGet = 0;//能取得的最大宝石数
		//输入的宝石顺序的第一个开始寻找一段序列,并且逐渐移动指针
		for (int beginPtr = 0; beginPtr < strLength; ++beginPtr) {
			if (inputStr[beginPtr] >= 'A' && inputStr[beginPtr] <= 'E') {
				//如果头指针指向的宝石类型是特殊类型才进行寻找操作
				int flags[5] = { 0 };//由于标记五种宝石是否找到
				int endPtr = beginPtr;//尾指针,
				int cnt = 0;//选中的宝石序列中宝石个数
				int sumFlag = 0;//序列中已经找到的特殊宝石总数
				while (sumFlag < 5 && cnt < strLength) {
				//序列中已找到的特殊宝石总数小于5,且序列中的宝石数小于宝石总数
					if (inputStr[endPtr % strLength] >= 'A' && inputStr[endPtr % strLength] <= 'E' && flags[inputStr[endPtr % strLength] - 'A'] == 0) {
						//如果尾指针指向的宝石(即将添加的宝石)是特殊宝石,且该宝石之前没有遇到
						flags[inputStr[endPtr % strLength] - 'A'] = 1;//标记已找到
						++sumFlag;//找到的特殊宝石总数自增
					}
					++cnt;//不管有没有找到,宝石序列都增加了一个长度
					++endPtr;//尾指针后移
				}
				if (maxGet < strLength - cnt) {
					//如果上一次寻找能获得的最大宝石数小于此次获得的,则更新
					maxGet = strLength - cnt;
				}
			}
		}
		//输出结果
		cout << maxGet << endl;
	}
	return 0;
}

如果代码有错误,或者有更好的思路,欢迎指正或指导。
小男子不才,前排大佬让一让。

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/85234670