先说一下思路:
这种做法有部分测试用例过不去。想了很久不知道原因,求高手指点。
题目是要找连续的长度为26的子串,并且这个子串包含A-Z所有的字母(当然肯定是每个字母只出现一次)。
我的思路有点类似与上一篇文章那个求最大子序列和的在线法。
首先我开一个数组用来记每个字母有没有出现过,从第一个开始遍历,分两种情况,要么遇到‘?’,要么遇到字母。如果遇到字母,对每一个字母若出现过,那么把上一个位置(假设是位置i)的这个字母以及以前的字母全都标记为未出现过,即扔掉从i位置到上一次i(这里注意,上一次的i每次都会用lasti记录,第一次lasti肯定是0,以后每次都是lasti=i+1)位置之间的序列,只保留余下的部分;接着把当前访问的这个字母标记为一出现,总数加一。
如果遇到问号就记录数目即可。
途中如果字母数加问号数等于26,那么就可以退出循环了,因为已经找到了所求子串。
如果符合条件,则填充的时候,把没出现过的依次填进去就好(一定是从lasti填到lasti+26),然后再从头遍历一遍,把其他的‘?’都填成任意一个字母即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
int main(){
string a;//记原字符串
cin >> a;
int cnt = 0,sum = 0;
int len = a.length();
int c[200];//记每个字母的位置
int book[26]={};//记已出现的字母
int i=0;
int lastj=0;//上一次重复出现的位置
while(len--){
if(a[i]=='?'){
cnt++;
i++;
}
else{
if(book[a[i]-65]==1){//处理重复以前的字母
int j = c[a[i]];//
for(int k=j;k>=lastj;k--){
if(a[k]=='?'){
cnt--;
}else{
book[a[k]-65]=0;
sum--;
}
}
lastj = j+1;//
}
c[a[i]]=i;
book[a[i++]-65]=1;
sum++;
if(sum+cnt >= 26)//子串已找到
break;
}
}
int lj=0;
for(int k=lastj;k<lastj+26;k++){//填“?”
if(a[k]=='?'){
int j;
for(j=lj;j<26;j++){
if(book[j]==0)
break;
}
a[k]=j+65;
lj=j+1;//
}
}
for(int j=0;j<a.length();j++){
if(a[j]=='?')
a[j]='A';
}
if(sum+cnt>=26)
cout << a;
else
cout << -1;
return 0;
}
下面是简单的暴力求解,两层for循环解决问题。
思路很简单,把每一组26的子串都跑一遍,有重复就break,直到找到没有重复的26个连续子串(‘?’不算重复)。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
int main(){
string a;
cin >> a;
int book[26]={};
int i,j;
int flag=0;
for(i=0;i<a.length();i++){
flag=0;
for(j=i;j<i+26;j++){
if(a[j]!='?'){
if(book[a[j]-65]==1){
flag=1;
break;
}
book[a[j]-65]=1;
}
}
if(flag==0){
flag=2;
break;
}
memset(book,0,26*sizeof(int));
}
int m=0;
if(flag==2){
for(int k=i;k<i+26;k++){
if(a[k]=='?'){
for(int h=m;h<26;h++){
if(book[h]==0){
a[k]=h+65;
m=h+1;
break;
}
}
}
}
for(int k=0;k>a.length();k++){
if(a[k]=='?')
a[k]='A';
}
cout << a;
}else cout << -1;
return 0;
}