栈
进出在同一侧,且满足后进先出,先进后出
车厢调度(train)
有一个火车站,铁路如图所示,每辆火车从A驶入,再从B方向驶出,同时它的车厢可以重新组合。假设从A方向驶来的火车有n节(n≤1000),分别按照顺序编号为1,2,3,…,n。假定在进入车站前,每节车厢之间都不是连着的,并且它们可以自行移动到B处的铁轨上。另外假定车站C可以停放任意多节车厢。但是一旦进入车站C,它就不能再回到A方向的铁轨上了,并且一旦当它进入B方向的铁轨,它就不能再回到车站C。
负责车厢调度的工作人员需要知道能否使它以a1,a2,…,an的顺序从B方向驶出,请来判断能否得到指定的车厢顺序。
解析:
观察发现,整个调度过程其实是在模拟入栈出栈的过程,而这个过程中,我们可以分成三种状态:栈前、栈中、栈后。我们可以发现,当某个数字出栈了,说明比它小的数字要么已经出栈了,要么还在栈里,不能是入栈前状态,并且在栈中的顺序是从大到小的(从栈顶往栈底看),比如出5,那么1,2,3,4要么已经在5之前出了,要么还在栈中(假如1,3,4在栈中,从栈顶往栈底看依次为4,3,1),不能是入栈前的状态。如果某个数字要出栈,那么当前在栈中的数字都必须小于它,否则就与栈的性质矛盾,不合法,于是我们可以这样解决:
从第一个数字开始扫描,a[i]表示当前出栈的数字,如果有比a[i]大的数字还在栈中,那么就产生矛盾,输出“NO”;否则,标记当前数字a[i]为栈后状态,那么[1, a[i]-1]这些数字如果还没出栈,标记为栈中状态。具体我们可以用0表示为确定状态,1表示栈中状态,2表示栈后状态。
#include<cstdio>
#include<stack>
using namespace std;
stack<int> liu;
int n,a[1005],b[1005];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(b[i]==j) a[j]=2;
if(a[j]==2) continue;
else if(b[i]>j) {
if(a[j]==0) a[j]=1;
}
else if(b[i]<j) {
if(a[j]>0) {
printf("NO");
return 0;
}
}
}
}
printf("YES");
}
字符串匹配问题(strs)
字符串中只含有括号 (),[],<>,{},判断输入的字符串中括号是否匹配。如果括号有互相包含的形式,从内到外必须是<>,(),[],{},例如。输入: [()] 输出:YES,而输入([]),([)]都应该输出NO。
解析:简历四个栈,记录四种符号
- 由于有优先级,所以当一个右括号匹配时,比他优先级低的括号必须已经匹配完成才行,左括号入栈即可,最后判断是否匹配完
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
stack<int> a,b,c,d;
char s[300];
int n,len,flag;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
flag=1;
scanf("%s",s);
len=strlen(s);
for(int i=0;i<len;i++) {
if(s[i]=='{') {
a.push(1);
}
if(s[i]=='}') {
if(a.empty()==1) {
flag=0;
break;
}
a.pop();
if(b.empty()==0||c.empty()==0||d.empty()==0) {
flag=0;
break;
}
}
if(s[i]=='[') {
b.push(1);
}
if(s[i]==']') {
if(b.empty()==1) {
flag=0;
break;
}
b.pop();
if(c.empty()==0||d.empty()==0) {
flag=0;
break;
}
}
if(s[i]=='(') {
c.push(1);
}
if(s[i]==')') {
if(c.empty()==1) {
flag=0;
break;
}
c.pop();
if(d.empty()==0) {
flag=0;
break;
}
}
if(s[i]=='<') {
d.push(1);
}
if(s[i]=='>') {
if(d.empty()==1) {
flag=0;
break;
}
d.pop();
}
}
//判断是否匹配完
if(a.empty()==0||b.empty()==0||c.empty()==0||d.empty()==0) {
flag=0;
}
if(flag) printf("YES\n");
else {//多组数据,要清空
printf("NO\n");
while(a.empty()==0) a.pop();
while(b.empty()==0) b.pop();
while(c.empty()==0) c.pop();
while(d.empty()==0) d.pop();
}
}
}
中缀表达式值(expr)
前缀和后缀表达式都很容易求值,我们将中缀表达式转换成后缀表达式,且后缀表达式不需要括号。复杂度O(n)
- 建立一个符号栈,一个数字栈
- 如果遇到一个数,加入数字栈
- 如果遇到左括号,把左括号入符号栈
- 如果遇到右括号,不断取出符号栈顶并在数字栈中运算,直到栈顶为左括号,把左括号出栈
- 如果遇到运算符,**只要栈顶符号的优先级不低于新符号,就不断取出栈顶并运算,最后把新括号入栈。**优先级乘除乘方>加减>左括号
- 最终数字栈剩下的数字就是结果。
分析:第五点保证了先进先出(优先级相同时)以及符号相互的优先级,第四点其实就是处理括号内的运算,在符号栈中越先出去的,在后缀运算中优先级更高
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
using namespace std;
//stack<char> f;
const int MAXN=1e6+5;
char a[MAXN],f[MAXN];
long long n,m,t,b[MAXN];
int main() {
scanf("%s",a);
n=strlen(a);
for(int i=0;i<n;i++) {
if(a[i]>='0'&&a[i]<='9') {
int w=0;
while(a[i]>='0'&&a[i]<='9') w=w*10+(a[i]-'0'),i++;
b[++m]=w;
}
if(a[i]=='(') f[++t]='(';
else if(a[i]==')') {
while(f[t]!='(') {
char x=f[t];
t--;
if(x=='+') b[m-1]=b[m-1]+b[m],m--;
if(x=='-') b[m-1]=b[m-1]-b[m],m--;
if(x=='*') b[m-1]=b[m-1]*b[m],m--;
if(x=='/') b[m-1]=b[m-1]/b[m],m--;
if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
}
t--;
}
else if(a[i]=='*'||a[i]=='/'||a[i]=='^') {
while(f[t]=='*'||f[t]=='/'||f[t]=='^') {
char x=f[t];
t--;
if(x=='*') b[m-1]=b[m-1]*b[m],m--;
if(x=='/') b[m-1]=b[m-1]/b[m],m--;
if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
}
f[++t]=a[i];
}
else if(a[i]=='+'||a[i]=='-') {
while(f[t]=='+'||f[t]=='-'||f[t]=='*'||f[t]=='/'||f[t]=='^') {
char x=f[t];
t--;
if(x=='*') b[m-1]=b[m-1]*b[m],m--;
if(x=='/') b[m-1]=b[m-1]/b[m],m--;
if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
if(x=='+') b[m-1]=b[m-1]+b[m],m--;
if(x=='-') b[m-1]=b[m-1]-b[m],m--;
}
f[++t]=a[i];
}
/*for(int i=1;i<=m;i++) printf("%d ",b[i]);
printf("\n");*/
}
for(int i=t;i>=1;i--) {
char x=f[i];
if(x=='+') b[m-1]=b[m-1]+b[m],m--;
if(x=='-') b[m-1]=b[m-1]-b[m],m--;
if(x=='*') b[m-1]=b[m-1]*b[m],m--;
if(x=='/') b[m-1]=b[m-1]/b[m],m--;
if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--;
}
printf("%lld",b[1]);
}
括号画家
写累了,直接看代码吧:
错误代码
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
stack<int> f;
const int MAXN=100005;
char s[MAXN];
int n,ans,t;
int main() {
scanf("%s",s);
n=strlen(s);
for(int i=0;i<n;i++) {
t++;
if(s[i]=='('||s[i]=='['||s[i]=='{') f.push(s[i]);
else if(f.empty()==1||(s[i]==')'&&f.top()!='(')||(s[i]==']'&&f.top()!='[')||(s[i]=='}'&&f.top()!='{')) {
t=0;
while(f.empty()==0) f.pop();
}
else f.pop();
int m=f.size();
ans=max(ans,t-m);
}
printf("%d",ans);
}
LJS的数据:
( ( ( { } [ ] ( ( ( )
正确输出:4
我的输出:6
为什么会这样呢?会发现我并没有考虑多出来的没有匹配的括号,计算时去掉了f.size()即栈的长度,如果在最左边,这种算法是成立的;而如果未匹配到的在中间,就是不满足题意,而我相当于把两个满足要求的子序列合并在一起了。只需判断当前子序列末尾与栈的是否完全相同即可
正解:
#include<cstdio>
#include<cmath>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
stack<int> f;
const int MAXN=100005;
char s[MAXN];
int n,ans,t;
int main() {
scanf("%s",s);
n=strlen(s);
for(int i=0;i<n;i++) {
t++;
if(s[i]=='('||s[i]=='['||s[i]=='{') f.push(s[i]);
else if(f.empty()==1||(s[i]==')'&&f.top()!='(')||(s[i]==']'&&f.top()!='[')||(s[i]=='}'&&f.top()!='{')) {
//说明以后的子序列不能延续这以前的部分了,直接删除
t=0;
while(f.empty()==0) f.pop();
}
else f.pop();
int m=f.size();
bool flag=1;
if(m!=0) {
int b[MAXN]={},len=0;
while(f.empty()==0) {
b[++len]=f.top();
f.pop();
}
for(int j=i-t+1;j<=i-t+m;j++) {
if(s[j]!=b[m-(j-(i-t+1))]) flag=0;
}
for(int i=len;i>=1;i--) f.push(b[i]);
if(flag) ans=max(ans,t-m);
}
if(flag) ans=max(ans,t-m);
}
printf("%d",ans);
}
队列
进出在异侧,且满足先进先出,后进后出
#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
//队列
//上次没有在操作完后统计,必须操作完后再统计
const int MAXN = 105;
queue<int> a[MAXN];
int x, ans, b[MAXN];
char c[MAXN];
//这道题从一边进入,另一边取出,且进入和取出的方向是固定的,所以用队列
//因为栈是从同一边进入和取出
int main() {
for (int i = 1; i <= 12; i++) {
for (int j = 1; j <= 4; j++) {
cin >> c[j];
if (c[j] == '0')
c[j] = 10;
else if (c[j] > '0' && c[j] <= '9')
c[j] -= '0';
else if (c[j] == 'A')
c[j] = 1;
else if (c[j] == 'J')
c[j] = 11;
else if (c[j] == 'Q')
c[j] = 12;
else if (c[j] == 'K')
c[j] = 13;
}
for (int j = 4; j >= 1; j--) a[i].push(c[j]);
//注意队列的方向
}
for (int j = 1; j <= 4; j++) {
cin >> c[j];
if (c[j] == '0')
c[j] = 10;
else if (c[j] > '0' && c[j] <= '9')
c[j] -= '0';
else if (c[j] == 'A')
c[j] = 1;
else if (c[j] == 'J')
c[j] = 11;
else if (c[j] == 'Q')
c[j] = 12;
else if (c[j] == 'K')
c[j] = 13;
a[13].push(c[j]);
//注意队列的方向
}
for (int i = 1; i <= 4; i++) {
x = a[13].front(), a[13].pop();
while (1) {
if (x >= 100)
x -= 100;
if (x == 13) {
break;
}
int t = x;
a[t].push(x + 100);
x = a[t].front();
a[t].pop();
}
}
for (int i = 1; i <= 12; i++) {
while (a[i].empty() == 0) {
int t = a[i].front();
a[i].pop();
if (t > 100)
b[t - 100]++;
}
}
for (int i = 1; i <= 12; i++) {
if (b[i] == 4)
ans++;
}
printf("%d", ans);
}