7-42 堆栈操作合法性 (20 分) 两种做法(模拟)/ (差分) 差分思维的应用(从0开始详细讲)

题目链接
假设以S和X分别表示入栈和出栈操作。如果根据一个仅由S和X构成的序列,对一个空堆栈进行操作,相应操作均可行(如没有出现删除时栈空)且最后状态也是栈空,则称该序列是合法的堆栈操作序列。请编写程序,输入S和X序列,判断该序列是否合法。

输入格式:
输入第一行给出两个正整数N和M,其中N是待测序列的个数,M(≤50)是堆栈的最大容量。随后N行,每行中给出一个仅由S和X构成的序列。序列保证不为空,且长度不超过100。

输出格式:
对每个序列,在一行中输出YES如果该序列是合法的堆栈操作序列,或NO如果不是。
输入样例:

4 10
SSSXXSXXSX
SSSXXSXXS
SSSSSSSSSSXSSXXXXXXXXXXX
SSSXXSXXX

输出样例:

YES
NO
NO
NO

这道题其实考察的是栈的应用,考察了栈的入栈操作, 出栈操作, 以及判断栈是否Full , 以及是否栈空删除 , 这是栈的基本操作

这是栈的定义

typedef int ElementType;
typedef enum { push, pop, end } Operation;
typedef enum { false, true } bool;
typedef int Position;
typedef struct SNode *PtrToSNode;
struct SNode {
    ElementType *Data;  /* 存储元素的数组 */
    Position Top;       /* 栈顶指针       */
    int MaxSize;        /* 堆栈最大容量   */
};
typedef PtrToSNode Stack;

下面是入栈的模板, 我们可以看到每次加入一个新的元素 , 都会加入到栈的尾部 ,如果达到栈的最大空间则就输出Stack Full 。

注意:栈是一种插到尾部,然后优先从尾部取出元素。

bool Push( Stack S, ElementType X )
{
	if(S -> Top == S -> MaxSize) 
	{
		puts("Stack Full") ;
		return false ;
	}
	S -> Data[(S -> Top) ++] = X ;
	return true ;
}

然后是出栈的模板,这里第一步先判断栈是否为空, 从尾部删掉节点, 然后完成栈的删除操作 。

ElementType Pop( Stack S )
{
	if(S -> Top == 0)
	{
		puts("Stack Empty") ;
		return ERROR ;
	}
	return S -> Data[-- (S -> Top)] ;
}

这里我们想一想怎么去进行差分操作。
首先,我们要是想满足栈为空的时候不被删除,首先我们想想,我们一定要满足S的数量大于X才行。
为什么 ?
因为加的元素比删的元素多才能满足上述条件 , 同时,不应该只针对最后一个下标满足此要求, 而是针对所有下标都应满足S的数量大于X

第二我们考虑一下怎么解决栈满问题, 我们有一个关键思维, 就是把 S 看成1 , X 看成 -1 , 然后对其进行求前缀和, 其前缀和就是当前栈里面存的元素的数量, 然后前缀和也不应该大于栈的空间。

第三最后满足完成所有的操作之后 , 栈为空 , 第二个条件我们可知 ,其前缀和就是当前栈里面存的元素的数量 , 我们是否可以根据前缀和为0 来 得出当前栈里面有几个元素。

没错, 是的, 如果最后一个元素的前缀和为0 , 则我们说他满足要求 , 反之则不满足要求 。

第一步我们先输入数据, 对其进行求前缀和, 把 S 看成 1 , X 看成 -1 。

		scanf("%s" , s) ;
		int len = strlen(s) ;
		for(int i = 1 ; i <= len ; i ++) pre[i] = pre[i - 1] + ((s[i - 1] == 'S') ? 1 : - 1) ;

第三句同下段代码作用相同

		for(int i = 1 ; i <= len ; i ++)
			if(S[i - 1] == 'S')
				pre[i] = pre[i - 1] + 1 ;
			else
				pre[i] = pre[i - 1] - 1 ;

第二
然后我们开始遍历前缀和,如果前缀小于0 , 或者大于m则不满足条件(解释上文所示)

		bool success = true ;
		for(int i = 1 ; i <= len ; i ++)
			if(pre[i] < 0 || pre[i] > m)
			{
				success = false ;
				break ;
			}

第三
我们判断最后一个条件

		if(pre[len] != 0) success = false ;

最后整个代码如下

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std ;
const int N = 1e5 + 10 ;
int pre[N] ;

int main(void)
{
	int T , m ;
	char s[N] ;
	scanf("%d%d" , &T , &m) ;
	while(T --)
	{
		scanf("%s" , s) ;
		int len = strlen(s) ;
		for(int i = 1 ; i <= len ; i ++) pre[i] = pre[i - 1] + ((s[i - 1] == 'S') ? 1 : - 1) ;
		bool success = true ;
		for(int i = 1 ; i <= len ; i ++)
			if(pre[i] < 0 || pre[i] > m)
			{
				success = false ;
				break ;
			}
		if(pre[len] != 0) success = false ;
		if(success) puts("YES") ;
		else puts("NO") ;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值