week8-csp-B(HRZ学英语)

题意

瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想
到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,
这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字
符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字
母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现
的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果
不存在,输出-1! 这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮
他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二
个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排
在前。例如

AB??EFGHIJKLMNOPQRSTUVWXYZ

ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是 第一个出现的,字典序最小的!

输入格式:输入只有一行,一个符合题目描述的字符串。
输出格式:输出只有一行,如果存在这样的子串,请输出,否则输出-1

样例1:
input:
ABC??FGHIJK???OPQR?TUVWXY?
output:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
样例2:
input:
AABCDEFGHIJKLMNOPQRSTUVW??M
ouput:
-1

思路

  • 我们输入只有一个字符串,长度大于26,需要得到的是最先出现的一个长度为26的字字符串,我们想到肯定是从开始遍历进行判断,每次判断26个字母,故使用尺取法进行,会比较便利;
  • 尺取法只需要每次得到26个字母即可,对于这26个字母,我们使用两个map变量进行一个字符和数字的转化:
    map<char, int> a;     //每一个字符代表的数字
	map<int, char> b;   //表示每一个字符
	a['A'] = 1; b[1] = 'A';
	a['B'] = 2; b[2] = 'B';
	a['C'] = 3; b[3] = 'C';
	a['D'] = 4; b[4] = 'D';
	a['E'] = 5; b[5] = 'E';
	a['F'] = 6; b[6] = 'F';
	a['G'] = 7; b[7] = 'G';
	a['H'] = 8; b[8] = 'H';
	a['I'] = 9; b[9] = 'I';
	a['J'] = 10; b[10] = 'J';
	a['K'] = 11; b[11] = 'K';
	a['L'] = 12; b[12] = 'L';
	a['M'] = 13; b[13] = 'M';
	a['N'] = 14; b[14] = 'N';
	a['O'] = 15; b[15] = 'O';
	a['P'] = 16; b[16] = 'P';
	a['Q'] = 17; b[17] = 'Q';
	a['R'] = 18; b[18] = 'R';
	a['S'] = 19; b[19] = 'S';
	a['T'] = 20; b[20] = 'T';
	a['U'] = 21; b[21] = 'U';
	a['V'] = 22; b[22] = 'V';
	a['W'] = 23; b[23] = 'W';
	a['X'] = 24; b[24] = 'X';
	a['Y'] = 25; b[25] = 'Y';
	a['Z'] = 26; b[26] = 'Z';
	a['?'] = 27; b[27] = '?';
  • 使用a变量,以及一个c数组(c数组的下标对应的是a数组的值),通过循环得到每一个字符的数目
    for (int i = left; i <= right; i++) //left=0,right=25,这是尺取法初始化的区间端点
	{
		c[a[s.at(i)]]++;//初始化C数组
	}
  • 尺取的26个字符会出现 ?,那么就会有26个字母没有出现的,我们使用?进行代替这些没有出现的,且字典序最小,那我们先找出没出现的字母,将其的索引,也就是b中的值存储在队列中;
	for (int i = 1; i <= 26; i++)
		{
			//存放不存在的字符
			if (c[i] == 0) {  //此处的i就是b数组中字符对应的值
				q.push(i);
				count++;
			}
		}
  • 那我们何时跳出尺取?如果?的数目等于没出现的字符数目,我们便可以跳出;
  • 跳出尺取后,我们要进行输出,输出26个,?用字符替换,那么替换的时候,需要使用b数组,下标就是我们取队首元素即可
  • 由于队列时FIFO结构,所以自然有序;

总结

对于此题,每次需要判断的是一个长度为26的区间,且区间需要进行移动,所以尺取法就很不错,但是此时的尺取法是伪尺取法,因为左右区间同步移动;

实验中错误

以为在未出现字符数目count==c[27]的时候还要进行判断是否有一个元素出现两次,首先这是不可能的;之前没有意识到,进行重复的判断,然后中途出错了。

代码

#include<iostream>
#include<string>
#include<map>
#include<queue>
using namespace std;
int main()
{
	map<char, int> a;     //每一个字符代表的数字
	map<int, char> b;   //表示每一个字符
	int c[28];  //用来存储每一个字符的数目;
	int cot=-1;//不存在输出;
	bool exist = false;
	a['A'] = 1; b[1] = 'A';
	a['B'] = 2; b[2] = 'B';
	a['C'] = 3; b[3] = 'C';
	a['D'] = 4; b[4] = 'D';
	a['E'] = 5; b[5] = 'E';
	a['F'] = 6; b[6] = 'F';
	a['G'] = 7; b[7] = 'G';
	a['H'] = 8; b[8] = 'H';
	a['I'] = 9; b[9] = 'I';
	a['J'] = 10; b[10] = 'J';
	a['K'] = 11; b[11] = 'K';
	a['L'] = 12; b[12] = 'L';
	a['M'] = 13; b[13] = 'M';
	a['N'] = 14; b[14] = 'N';
	a['O'] = 15; b[15] = 'O';
	a['P'] = 16; b[16] = 'P';
	a['Q'] = 17; b[17] = 'Q';
	a['R'] = 18; b[18] = 'R';
	a['S'] = 19; b[19] = 'S';
	a['T'] = 20; b[20] = 'T';
	a['U'] = 21; b[21] = 'U';
	a['V'] = 22; b[22] = 'V';
	a['W'] = 23; b[23] = 'W';
	a['X'] = 24; b[24] = 'X';
	a['Y'] = 25; b[25] = 'Y';
	a['Z'] = 26; b[26] = 'Z';
	a['?'] = 27; b[27] = '?';
	for (int i = 1; i <=27; i++)
		c[i] = 0;
	string s;
	cin >> s;
	//使用尺取法进行求取;
	int left = 0, right = 25;
	for (int i = left; i <= right; i++)
	{
		c[a[s.at(i)]]++;//初始化C数组
	}
	queue<int>  q;//存放不存在的字符
	while (right < s.size())
	{
		while (!q.empty())
			q.pop();
		int count = 0;//用来记录不存在字符数目
		for (int i = 1; i <= 26; i++)
		{
			//存放不存在的字符
			if (c[i] == 0) {
				q.push(i);
				count++;
			}
		}
		if (c[27] == count)
		{	
			exist = true;
			break;
		}
		else
		{
			cot = -1;
		}
		c[a[s.at(left)]]--;
		left++;
		right++;
		if (right == s.size())
			break;
		c[a[s.at(right)]]++;
	}
	if (exist) {
		for (int i = left; i <= right; i++)
		{
			if (s.at(i) != '?')
				cout << s.at(i);
			else
			{
				int temp = q.front();
				cout << b[temp];
				q.pop();
			}
		}

	}
	else
		cout << cot;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值