第二周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

ABC??FGHIJK???OPQR?TUVWXY?

**样例输出1

ABCDEFGHIJKLMNOPQRSTUVWXYZ

**样例输入2 ****

AABCDEFGHIJKLMNOPQRSTUVW??M

在这里插入图片描述
二、思路概述

  • 这一题初始的想法就是尺取法。但是具体的思路不太对。我想的是,对这个字符串进行处理,每次将一个字符作为开头,从开头开始往后遍历,如果碰到了重复的字符,就从重复的字符重新开始,一直到遍历完字符串或者找到了不重复的26个字符,但是复杂度过高,超时。
  • 后来改变了思路,也是从一个字符开始,判断以这个字符为开头的26个字符是否满足条件,若不满足,则整个区间后移一位,再次判断。。。。。
  • 对于“?”可记录下“?”的位置,找出区间后,看此区间还有哪些字母没有出现,按字母序顺序进行安排这些字母的位置,取代“?”

三、细节

  1. 最初的算法中,思路最开始不对,若现在判断的区间里面,1处有一个a,2处也有一个a,造成重复,那么重新开始的点应该是1处a的后一个位置才对,但我最初的想法是从2处开始。

四、完整代码

  1. 初始代码
/*复杂度过高,思路有问题,好像不能优化
铭记错误*/ 

#include<iostream>
#include<string>
#include<cstring> 
using namespace std;

struct vis{
	int b;
	int num;
};

int ques[26];//记录问号的位置 
vis v[26];//记录此时字母是否存在
bool bl=0;
int zl,zr;
 
bool dfs(int l,int r,string s)
{
	for(int i=0;i<26;i++)
	{
		v[i].b =0;
		v[i].num=-1;
		ques[i]=-1;
	}
		
	int q=-1; 
	if(s[l]=='?')
	{
		q++;
		ques[q]=r;//r位置有一个?
	}
	else
	{	
	int x=s[l]-'A';
	v[x].b=1;
	v[x].num =l;
	}
	while(r<s.size()-1)
	{	
		r++;
		if(s[r]=='?')
		{
			q++;
			ques[q]=r;//r位置有一个? 
		}
		
		else//字母 
		{
			
		int rr=s[r]-'A';
		
		if(v[rr].b ==0)//这个字母没有出现过
		{
			v[rr].b=1; 
			v[rr].num =r;
	    }
		else//此字母到达过
		{	
		    int xx=v[rr].num+1;	
			//dfs(xx,xx,s);
			return xx;
		} 
		
		}
		
			
		if(r-l+1==26) //找到了字符串 
	    {
	    	zr=r;
	    	zl=l;
	    	bl=1;
	    	return -1;
		}    
	}
	
	if(r-l+1!=26)
	return -2;
	
}

int main()
{
	string s;
	cin>>s;

  if(s.size()<26) 
  {
  	cout<<-1<<endl;
  }
  else
  {
  	int l=0;
  	int r=0;
  	while(bl==0)
  	{
  		int x=dfs(l,r,s);
  		if(x==-2)
  		break;
		if(x!=-1&&x!=-2) 
		{
			l=x;
			r=x;
		}
	}
	    

	if(bl==0)
	cout<<-1;
	else
	{
	int num=0;
	for(int i=0;i<26;i++)
	{
	    if(v[i].b==0)//把问号的部分转化为 
		{
		s[ques[num]]=char(i+65);
		num++;
		}	    		
	}
						
		for(int ii=zl;ii<=zr;ii++)
			cout<<s[ii];	
    }
    cout<<endl;
}
   return 0;
}
  1. 复杂度降低的代码
#include<iostream>
#include<string>
#include<cstring> 
using namespace std;

struct vis{
	int b;
	int num;
};

string s;
int ques[26];//记录问号的位置 
vis v[26];//记录此时字母是否存在
//bool bl=0;
int zl;
 
bool judge(int l)//从l开始的大小为26的区间是否满足条件 
{
	for(int i=0;i<26;i++)
	{
		v[i].b =0;
		v[i].num=-1;
		ques[i]=-1;
	}
	
	int q=-1; 
	for(int i=l;i<l+26;i++)
	{
		if(s[i]=='?')
		{
		    q++;
		    ques[q]=i;//r位置有一个?
	    }
	    else if(s[i]<='Z'&&s[i]>='A')
	    {
	    	int rr=s[i]-'A';
		
		    if(v[rr].b ==0)//这个字母没有出现过
		    {
			v[rr].b=1; 
			v[rr].num =i;
	        }
	        else
	        {
	        	zl=v[rr].num+1;
	        	return false;
			}
		}
	}
	return true;
}

int main()
{
	
	cin>>s;

  	int l=0;
  	//cout<<s.size() <<endl;
  	while(1)
  	{
  		int bl=judge(l);
  				
  		if(bl==1)
  		{
  			//cout<<l<<"l"<<endl;
  			
		    int num=0;
	        for(int i=0;i<26;i++)
	        {
	            if(v[i].b==0)//把问号的部分转化为字母 
		        {
		        s[ques[num]]=char(i+65);
		        num++;
		        }	    		
	        }
						
		    for(int ii=l;ii<l+26;ii++)
			    cout<<s[ii];
		    cout<<endl;	
			
			break; 
        }
        
        else
        {
        	//cout<<l<<"meiyou"<<endl;
		    if(l==s.size()-26)
  		    {
  			   cout<<-1<<endl;
  			   break;
		    }
		    //cout<<zl<<endl;
        	l=zl;
		}
    
	}
	
  	
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值