题目描述
给定一个字符串,包括26个大写字母和“?”,其中?可以代表任意一个字母,现要求你在这个字符串中找到第一个连续的包含26个大写字母的子串(可以有问号),并输出字典序最小的那一个。
Input
输入只有一行,一个符合题目描述的字符串。
Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1
解题思路
思路很简单,用一个长度为26的移动框,在输入字符串上移动,每次移动一个字符,然后判断此时框内的子串是否符合题目要求,因为要求的输出第一个满足条件的子串,因此只要符合条件,输出这个框内的字典序最小的情况即可,如果不存在输出-1.
具体细节:
- 用vis数组来标记当前框内每个字母的个数,如vis[0]代表A的个数
- 用index数组来标记当前框内每个问号的下标,如index[i]代表当前框内第i个问号的下标
- 用cnt记录当前框内重复字母的数量,一直到cnt=0就可以输出了;
- 问号变字母的时候,从A到Z遍历找到每一个vis为0的字母,然后顺序将每个问号变为这个字母,因为这个过程是顺序进行的,因此字典序一定是最小的。
实现代码
#include<iostream>
#include<string>
using namespace std;
int vis[26];
string s;
int index[27],num=0;
int main()
{
int l=0,r=25,cnt=0;
cin>>s;
for(int i=l;i<=r;++i)
{
if(s[i]=='?')
{
num++;
index[num]=i;
}
else
{
vis[s[i]-'A']++;
if(vis[s[i]-'A']>1)
cnt++;
}
}
if(cnt==0)
{
int t=1;
for(int i=0;i<26;++i)
{
if(vis[i]==0)
{
s[index[t]]=i+'A';
t++;
}
}
for(int i=l;i<=r;++i)
cout<<s[i];
}
else
{
while(r<s.size()-1)
{
if(cnt==0)
break;
if(s[l]=='?')
l++;
else if(vis[s[l]-'A']>1)
{
cnt--;
vis[s[l]-'A']--;
l++;
}
else if(vis[s[l]-'A']==1)
{
vis[s[l]-'A']--;
l++;
}
r++;
if(s[r]=='?')
continue;
vis[s[r]-'A']++;
if(vis[s[r]-'A']>1)
cnt++;
}
if(r==s.size()-1&&cnt>0)
cout<<-1;
else
{
for(int i=l;i<=r;++i)
{
if(s[i]=='?')
{
for(int j=0;j<26;++j)
{
if(vis[j]==0)
{
s[i]=j+'A';
vis[j]++;
break;
}
}
}
}
for(int i=l;i<=r;++i)
cout<<s[i];
}
}
return 0;
}
总结
这道题是模测的时候做出来的,看到题目的时候就觉得很直观,实际做的时候还是挺曲折的,因为框每次移动后的删点和加点,根据该位置是字母还是问号处理方式是不一样的,所以处理起来脑子可能一时不够清晰(还是思路整理的不明白),但结果还是好的。