题目描述
瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想 到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想, 这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字 符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字 母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现 的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果 不存在,输出-1! 这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮 他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二 个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排 在前。例如
AB??EFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是 第一个出现的,字典序最小的!
输入输出
Input
输入只有一行,一个符合题目描述的字符串。
Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1
Sample Input
ABC??FGHIJK???OPQR?TUVWXY?
AABCDEFGHIJKLMNOPQRSTUVW??M
Sample Output
ABCDEFGHIJKLMNOPQRSTUVWXYZ
-1
思路分析
此题做法类似于滑动窗口,窗口大小为26。首先用sum[]数组记录每种字母和"?“的个数,然后遍历sum[]数组,若每个字母次数均未超过1次,则按字典序补齐”?"输出;否则,向后移动窗口(l++,r++)。
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=1e6+5;
char s[maxn],tmp[27];
int sum[27]={0};//记录窗口内每个字母出现次数
int l=0,r=25;//双指针
int check()
{//检查合法
for(int i=0;i<26;i++)
{//某个字母出现多于一次则不合法
if(sum[i]>1)
return 0;
}
return 1;
}
void output()
{
int index=0;
for(int i=0;i<26;i++)
{
if(sum[i]==0)
{//按字典序最小插入字母,存到tmp数组中
tmp[index]=char('A'+i);
index++;
}
}
index=0;
for(int i=l;i<=r;i++)
{//遍历数组s[],遇到"?"输出tmp[]中的字母,遇到字母则正常输出
if(s[i]=='?')
cout<<tmp[index++];
else cout<<s[i];
}
cout<<endl;
}
int main()
{
cin>>s;
int len=strlen(s);
for(int i=0;i<26;i++)
{
if(s[i]=='?') sum[26]++;
else{//从sum[0]开始存储每个字母出现次数
sum[s[i]-'A']++;
}
}
while(r<len){
if(check()){//合法则输出
output();
return 0;
}
else{//不合法,指针右移
if(s[l]=='?')
sum[26]--;
else{
sum[s[l]-'A']--;
}
l++;
r++;
if(r<len){
if(s[r]=='?')
sum[26]++;
else sum[s[r]-'A']++;
}
}
}
cout<<-1<<endl;
return 0;
}