题意
瑞神今年大三了,他在寒假学会了英文的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;
}