假设某火车站采用后进先出的模式。现有n列火车,调度人员给出火车进站的序列,并给出火车出站的序列,判断这个调度要求能否实现,如果能实现写出火车进站、出站的操作序列。
输入:第一行为一个正整数N代表火车数量;第二行为N个字母,中间用空格分开,代表N个火车的进站顺序;第三行为N个字母,中间用空格分开,代表N个火车的离站顺序。
输出:第一行输出0或1,0代表该调度无法实现,1代表可以实现;如果可以实现,请在第二行输出进站出站序列。表示进站时在字母后加上’_in’,出站加上’_out‘
输入样例1:
5
A B C D E
C D B A E
输出样例1:
1
A_in B_in C_in C_out D_in D_out B_out A_out E_in E_out
输入样例2:
5
A B C D E
E C D B A
输出样例2:
0
前置知识:合法出栈序列规则
对于出栈序列中的每一个数字,后边比他小的数字一定是降序排列的
举例而言,如果入栈顺序是1234,出栈序列2431,经过如下步骤
1_in 2_in 2_out 3_in 4_in 4_out 3_out 1_out
则说明2431可以达成。
而如果出栈序列是2413呢?
1_in 2_in 2_out 3_in 4_in 4_out
当到了4出栈这一步的时候,出栈序列里已经有了24,而栈里留下的是31,3位于栈顶,也就是说无论如何也无法在出栈序列中让1出现在3的前边,因为栈后进先出,3留在了1的上边。
这是因为,序号大的数字总是后入栈的,小的数字在出栈序列中位于大的数字前边当然可行,小数字进栈出栈就可以了,而大的数字呢?要么等小数字先入栈出栈,在序列中位于他的前边;要么小数字在序列中位于大数字后边,那就只能小数字先入栈待在栈中,等大的数字进栈出栈以后小数字再出栈,而我们提到,栈一定是后进先出的,比如说上边的2413,由于4在前边,那么13只能入栈后再栈里等着,而由于1比3靠前,肯定是1先入了栈,在栈中的位置比3靠下,那么,出栈的时候,就一定是3先出了栈1才有机会出栈。如此,出栈序列的排序,大数字的后边小数字就一定是降序的了。
这是转自另一个作者画的火车出入栈的描述图,很形象可以参考:
他的文章里写了基础情况火车出入栈的描述:
https://blog.csdn.net/jiayoudangdang/article/details/79313905
本题的解释
知道了上边的规则,我们就很容易能写出判断以1234为入栈顺序,输入一个出栈顺序合法性的算法了。
可是这个题呢,这个题要求我们输入入栈的字母顺序和出栈的字母顺序,这可怎么办呀,我们的规则是基于数字大小比较的,他变成了字母还能适用这个规则吗?
其实是一样的,我们前边的1234的入栈顺序指的是序号,也就是说,第一个入栈的火车为1,第二个为2,以此类推。而出栈的序列本质上是对于入栈的时候第几个入栈的车在出栈的时候放在什么位置(第几个出栈)的描述。换句话讲,1234是他们的序号,他们进行入栈这个动作的时候获得的属性,跟这个车叫什么,长什么样子,完全没有关系。
说到这里你应该明白了,我们输入入栈序列ABCDE,这个时候,A就是1,B就是2,C就是3,以此类推。而我们想要CDBAE这个出栈序列,也就是想要34215这个序列。那这样我们就将问题转化为了序号火车出入栈的问题。
上代码。
#include<iostream>
#include<stack>
using namespace std;
int main(){
int n;//c[n]存放由期待的字母表示的火车顺序转为的数字顺序
cin>>n;
char a[n],b[n];//n为火车的列数,a[n]存放输入的火车顺序,b[n]存放期望输出的火车顺序
int c[n];//c[n]用以存放翻译后期望顺序的数列
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++)
cin>>b[i];
for(int i=0;i<n;i++)//这一部分用于把字母化的期望输出序列转化为数字序号序列
for(int j=0;j<n;j++){
if(b[i]==a[j]){
c[i]=j;
}
}
int a1=0,a2=0;//用于表示入栈的顺序,从1开始,自增至n
stack<int>stack1,stack2;//栈stack1用于验证输出序列是否合法,stack2用于输出进出流程
bool flag=1;
for(int i=0;i<n;i++){
while(stack1.empty()==true||stack1.top()<c[i])
stack1.push(a1++);//如果栈为空,或者栈顶元素小于期望数列对应位置的序号,则对12345中还留着的第一个入栈
if(stack1.top()>c[i]){
flag=0;//如果栈顶元素已经大于下一个出栈位置的序号,因为入栈是从小到大序号入栈的,
break;//大序号已经在栈中,小序号只能是在栈的下部,已经不可能放到出栈序列中希望的位置
}
if(stack1.top()==c[i])
stack1.pop();//若栈顶元素等于出栈序列对应位置,则正好是进栈->栈顶->出栈
}
if(flag==0)
cout<<0<<endl;//先用第一部分的循环判定是否能达成期望的输出序列,若不能则直接输出0;若能,则重复类似操作并输出进出栈顺序
else{
cout<<1<<endl;
for(int i=0;i<n;i++){
while(stack2.empty()==true||stack2.top()<c[i]){
stack2.push(a2++);//a2从0开始,代表进栈序号从0-(n-1)的火车进栈了 ,进栈成功后a2++,代表下 一次判断对下一辆车进行,
cout<<a[a2-1]<<"_in ";// a2自增后移到了下一辆车,想输出这辆车进栈需a2-1
}
if(stack2.top()>c[i]){
flag=0;
break;
}
if(stack2.top()==c[i]){
stack2.pop();//与入栈同理
cout<<a[a2-1]<<"_out ";
}
}
}
return 0;
}