【括号匹配问题】与【放苹果问题】——递归的算法应用

括号匹配问题

问题描述

在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用“$”标注,不能匹配的右括号用“?”标注。

输入

输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超过100。

输出

对每组输出数据、输出两行,第一行包含原始输入字符,第二行由“$”,“?”和空格组成,“$”和“?”表示与之对应的左括号和右括号不能匹配。

Sample Input

((ABCD(x)

)(rttyy())sss)(

Sample Output

((ABCD(x)

$$

)(rttyy())sss)(

?                ?$

问题分析

这道题是在Coursera中的C语言课程中遇到的,属于递归函数的课后习题,很自然地,我想到用递归的方法去解答这道题,当然,之后才发现递归不是最佳解法。

网上关于本题已有不少样例代码,因此我将谈一下关于这道题我自己的做法。

在这里,我将搜寻与标记的函数放在了一个大循环内,以保证能够遍历整个字符串。在搜寻与标记的函数内,每当遇到一个左括号,我都会在左括号对应的位置标记“$”符号并递归搜寻,只有遇到了右括号后,才会将靠得最近的“$”符号删去,如果没有“$”符号,则标记“?”符号,以此达到整个标记的效果。这里值得注意的是,为了做到后续的删除,我就需要记下所有标记的“$”符号的位置。因此,这种方法的确稍显复杂与冗余。

第二种方法就是使用堆栈来存放左括号的位置,与我的上述方法很类似,只是我的方法需要不断地进行标记与删除,而使用堆栈的思想后,仅仅需要做堆栈的入和出即可记录未配对的左右括号的位置了,更加精妙。

代码

//方法一
/***********JSP ©copyright***********/
/*******Version 1.0 2018.08.16*******/
#include <stdio.h>
#include <string.h>

#define MAX_STRING_LENGTH 100

int leftFindMark[MAX_STRING_LENGTH] = { 0 };
int rightFindMark[MAX_STRING_LENGTH] = { 0 };
int point = 0;
int leftFindCount = 0;
int rightFindCount = 0;

void strMarkInit(char strMark[], char str[]);
void searchPair(char str[], char strMark[]);

void main(){
	char str[MAX_STRING_LENGTH], strMark[MAX_STRING_LENGTH];
	gets_s(str);
	strMarkInit(strMark, str);
	while (point >= 0 && point < strlen(str)){
		searchPair(str, strMark);
	}
	puts(strMark);
}

void strMarkInit(char strMark[], char str[]){
	unsigned int i = 0;
	for (i = 0; i < strlen(str); i++){
		strMark[i] = ' ';
	}
	strMark[i] = '\0';
}
void searchPair(char str[], char strMark[]){
	if (str[point] == '('){
		leftFindMark[leftFindCount++] = point;
		strMark[point] = '$';
		point++;
		searchPair(str, strMark);
	}
	else if (str[point] == ')'){
		rightFindMark[rightFindCount++] = point;
		if (leftFindCount > 0){
			rightFindCount--;
			rightFindMark[rightFindCount] = 0;
			strMark[leftFindMark[leftFindCount - 1]] = ' ';
			leftFindCount--;
			point++;
		}
		else{
			rightFindMark[rightFindCount++] = point;
			strMark[point] = '?';
			point++;
		}
		return;
	}
	else{
		point++;
	}
}


//方法二
/**************网络共享***************/
#include <stdio.h>
#include <string.h>
#include <stack>

int main(){
	int i = 0;
	char str[101], strMark[101];
	while (scanf("%s", str) != EOF){
		std::stack<char> S;
		for (i = 0; i < strlen(str); i++){
			if (str[i] == '('){
				S.push(i);
				strMark[i] = ' ';
			}
			else if (str[i] == ')'){
				if (S.empty()){
					strMark[i] = '?';
				}
				else{
					S.pop();
					strMark[i] = ' ';
				}
			}
			else{
				strMark[i] = ' ';
			}
		}
		while (!S.empty()){
			strMark[S.top()] = '$';
			S.pop();
		}
		strMark[i] = '\0';
		puts(str);
		puts(strMark);
	}
	return 0;
}

放苹果问题

问题描述

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的放法?(用K表示)

注意:5,1,1和1,5,1是同一种分法

输入数据

第一行是测试数据的数目t(0<=t<=20),以下每行均包含两个整数M和N,以空格分开。1<=M,N<=10。

输出要求

对输入的每组数据M和N,用一行输出相应的K。

输入样例

1

7 3

输出样例

8

问题分析

这是一道典型的递归题目,关键在于找到递归函数中的递归式与返回式以及递归的函数关系。

设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,

当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)

当n<=m:不同的放法可以分成两类:

1、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1);

2、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n)

而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n)

递归出口条件说明:

当n=1时,所有苹果都必须放在一个盘子里,所以返回1;

当没有苹果可放时,定义为1种放法;

递归的两条路,第一条n会逐渐减少,终会到达出口n==1;

第二条m会逐渐减少,因为n>m时,我们会return f(m,m) 所以终会到达出口m==0。

代码

#include<stdio.h>
int fun(int m, int n)
{
	if (m == 0 || n == 1)
		return 1;   
	if (n>m)
		return fun(m, m);
	else
		return fun(m, n - 1) + fun(m - n, n);
}

int main()
{
	int t, m, n;
	scanf_s("%d", &t);
	while (t--)
	{
		scanf_s("%d%d", &m, &n);
		printf("%d\n", fun(m, n));
	}
}

//#include<stdio.h>
//int sum;
//void fun(int m, int n, int max)
//{
//	if (m == 0 && n == 0)//递归成功出口
//	{
//		sum++;
//		return;
//	}
//	if (m<0 || n<0)
//		return;//递归失败出口
//	for (int i = max; i >= 0; i--){
//		fun(m - i, n - 1, i);//递归
//	}
//}
//int main()
//{
//	int T, m, n;
//	scanf_s("%d", &T);
//	while (T--)
//	{
//		sum = 0;
//		scanf_s("%d%d", &m, &n);
//		fun(m, n, m);
//		printf("%d\n", sum);
//	}
//	return 0;
//}

 

    原文作者:括号匹配问题
    原文地址: https://blog.csdn.net/ICE_KoKi/article/details/81748148
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞