魔王语言(C语言版)

问题描述:
魔王的语言是由以下两种形式的规则由人的语言逐步抽象上去的。
(1)α–>β(1)β(2)β(3)….β(m)
(2)(θδ(1)δ(2)δ(3)…δ(n))–>θδ(n)θδ(n-1)…θδ(1)θ
(括号内数字为下标),上面的规则中,从左到右表示将魔王的语言翻译成人类的语言。

设计要求:

魔王语言和人类语言按照该语法规则进行转换。设大写字母表示魔王语言词汇,小写字母表示人类语言词汇。上述的希腊文法式中,希腊字母表示可以用大写字母或小写字母代换的变量。魔王语言可以包含人类词汇。
(1)B–>tAdA
(2)A–>sae
编写一个魔王语言的翻译系统,把魔王的话翻译成人类语言。

例如:A(abcd)B可解释为saeadacabatsaedsae。

刚开始读题的时候其实我是一头雾水的,后来在网上查了查才理解这道题到底是要我干什么!

我在做这道题前参考虑几篇网上的文章的思路

我大致将这道题目分成三个模块

1、判断输入的字符串是否合法(可以使用栈来进行符号匹配,这里不再赘述),也可以想参考文章里一样比较左右括号的数量是否相等。

2、去括号,将输入字符串通过一次次循环的方式去掉所有括号为止(个人觉得这个部分是最难的),参考其他大佬的想法后我选择了使用多个栈(4个)的方式来做这道题,分别为存放括号左侧字符、括号内字符、括号右侧字符,剩下的一个是辅助栈。

3、翻译:我写了一个函数,把输入的兼有大小写的原始翻译结果进一步翻译成全小写的“人类语言”。

参考文章:

数据结构课程设计---魔王语言解释 - 新技术 - 博客园

ps:这里我建议学习c语言的同学们自己写一个栈文件,这样以后需要使用栈的时候直接导入就好了,只需要改改结构体和数据类型就好了。(c++就会方便许多,直接有现成的让你用)。

先讲一下第二步的思路:

我们有四个栈:S1,S2,S3,S4

首先,将字符串从尾到头读入S1栈(注意是从尾到头),之后在一个个抛出至S3栈,直到遇到第一个右括号,将其压入S2栈中,再将S3弹出并依次压入S2,直到遇到第一个左括号,再将其压入
S2,这样,我们就得到了最内层的第一重括号内的字符。

将栈S2的栈顶左括号弹出,设置一个变量e去保存这个栈中的第一个字符,然后将其他元素和e按照题目的要求依次压入栈S4中。

之后重复上述操作,直到去掉所有括号。

这里要注意:操作完之后原本括号前的数据的顺序是反的,要注意调回来。

上代码:

我为了图方便把所有方法都写进栈的头文件里了,大家为了规范性可以再建立头文件:

头文件:

#pragma once
#ifndef STACK_H
#define STACK_H
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct 
{
	char data;
	struct Node* next;
}Node;
typedef struct stack
{
	Node* Top;
	int size;
}Stack;
Stack *InitStack();
int EmptyStack(Stack *);
int LengthStack(Stack*);
void Push(Stack *s,char);
void DestroyStack(Stack* );
void Pop(Stack* s,char *e);
//以上都是栈的基本操作
int match(char* s);//检测是否匹配
char* translate(char* s,int len);//去括号
void Show(Stack*);//测试的时候查看结果用的
int exist(char*);//判断括号是否存在,控制循环结束
#endif // !STACK_H

stack.c

#include"stack.h"//方法实现函数 不带头结点
Stack *InitStack()//初始化
{
	Stack* stack;
	stack = (Stack*)malloc(sizeof(Stack));
	if (stack == NULL)
	{
		printf("创建失败");
		exit(0);
	}
	stack->size = 0;
	stack->Top = NULL;
	return stack;
}
int EmptyStack(Stack *stack)//栈为空返回1,否则返回0
{
	return stack->size==0;
}
int LengthStack(Stack* stack)
{
	return stack->size;
}
void Push(Stack* s, char e)
{
	Node* p;
	 p=(Node*)malloc(sizeof(Node));
	 if (p == NULL)
	 {
		 printf("创建失败");
		 exit(0);
	 }
	 p->data = e;
	 p->next = s->Top;
	 s->Top = p;
	 s->size++;
}
void Pop(Stack* s,char *e)
{
	Node* p;
	p = s->Top;
	*e = p->data;//指针的使用还是需要注意的,这个e一定要加*号,否则起不到赋值的作用
	s->Top = p->next;
	free(p);
	s->size--;
}

void DestroyStack(Stack* stack)
{
	Node* p;
	while (stack->Top != NULL)
	{
		p = stack->Top->next;
		free(stack->Top);
		stack->Top = p;
	}
	free(stack->Top);
}
void Show(Stack* S)
{
	Node* p;
	p = S->Top;
	while (p!= NULL)
	{
		printf("%c ", p->data);
		p = p->next;
	}
	printf("\n");
}

match.c

#include"stack.h"
//此文件中的主要函数是去解决输入的字符串括号是否匹配的问题,这和我们前几天写
//的一道字符匹配的题目比较相似,遇到'('直接入栈,遇到')'就抛出栈顶的元素,如果不是左括号,则不匹配
int match(char* s)
{
	Stack *S;
	S = InitStack();
	Node* r = (Node*)malloc(sizeof(Node));
	int i=0;
	while (s[i] != '\0')//遍历数组
	{
		if (S->size == 0)
		{
			if (s[i] == ')')//直接读入一个右括号,说明没有左括号,一定不匹配
			{
				return 0;
			}
			else if(s[i]=='(')
			{
				Push(S, s[i]);//如果读入的是左括号则直接入栈
			}
		}
		else//栈不空的情况
		{
			if (s[i] == '(')
			{
				Push(S, s[i]);
			}
			else if(s[i]==')')
			{
				Pop(S, r);
				if (r->data != '(')
				{
					return 0;
				}
			}
		}
		i++;
	}
	if (S->size != 0)//如果遍历完后栈里还有剩余的符号,一定有没有配对的情况存在
	{
		return 0;
	}
	return 1;
}
int exist(char* s)
{
	int i=0;
	while (s[i]!='\0')
	{
		if (s[i] == '(')
			return 1;
		i++;
	}
	return 0;
}

split.c

#include"stack.h"
//这部分函数是魔王语言的最主要部分2022.10.2
/*简单思想如下:首先,将字符串从尾到头入S1栈,之后在一个个抛出至S3栈,直到第一个
右括号,将其压入S2栈中,再将S3弹出并依次压入S2,直到遇到第一个左括号,再将其压入
S2,这样,我们就得到了最内层的第一重括号内的字符
ps 这里可能会有一些问题,我们在这里假设魔王问题的括号只有嵌套而没有并列(即同一层内有多
个括号) 应该也可以实现 这只是一个分离的操作 到时候试试就知道了
将栈S2的栈顶左括号弹出,设置一个变量e去保存这个栈中的第一个字符,然后将其他元素依次
压入栈S4中,在将e与S4中的元素依次压入S3中,之后重复上述操作
*/
char* translate(char* s,int len)
{
	Stack* S1;
	Stack* S2;
	Stack* S3;
	Stack* S4;
	S1 = InitStack();
	S2 = InitStack();
	S3 = InitStack();
	S4 = InitStack();
	char* r;
	char* e;
	char* S;
	r = (char*)malloc(sizeof(char));
	e = (char*)malloc(sizeof(char));
	int i;
	for (i = len-1; i >= 0; i--)
	{
		Push(S1, s[i]);
	}
	/*Show(S1);*/
	Pop(S1, r);
	while (*r!= ')')
	{
		Push(S3, *r);
		Pop(S1, r);
	}
	Push(S2, *r);//把右括号压入S2中
	do
	{
		Pop(S3, r);
		Push(S2, *r);
	} while (*r!= '(');
	//这样我们就得到了第一层内括号
	Pop(S2, r);//弹出左括号
	Pop(S2, e);//把第一个元素弹出并保存
	Pop(S2, r);
	while (*r!= ')')
	{
		Push(S4, *e);
		Push(S4, *r);
		Pop(S2, r);
	}
	Push(S4, *e);
	/*Show(S4);*/
	//最后一次会弹出右括号,就不用在放入栈S4里了
	S = (char*)malloc((S1->size + S3->size + S4->size+1) * sizeof(char));
	i = 0;
	while (!EmptyStack(S3))
	{
		Pop(S3, r);
		Push(S2, *r);
	}
	while (!EmptyStack(S2))
	{
		Pop(S2, r);
		S[i++] = *r;
	}
	while (!EmptyStack(S4))
	{
		Pop(S4, r);
		S[i++] = *r;
	}
	while (!EmptyStack(S1))
	{
		Pop(S1, r);
		S[i++] = *r;
	}
	S[i] = '\0';
	return S;
}

main.c

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include"stack.h";
//以下的两个函数是用递归写的魔王语言的最后翻译操作
void Maho(char* s)//把打开括号的小写和大写复合的语言化为全小写的魔王语言
{
	int i=0;
	while (s[i] != '\0')//判断字符是否读取完全
	{
		if (!isupper(s[i]))//判断大小写
	{
		printf("%c", s[i]);//只输出小写字符
	}
	else
	{
		toStr(s[i]);//把存在的大写字符翻译为小写字符
	}
		i++;//遍历的参数
	}
}
int toStr(char c)
{
	int j=0;
	char Str[5];
	if (c == 'A')
	{
		Str[j++] = 's';
		Str[j++] = 'a';
		Str[j++] = 'e';
	}
	else if (c == 'B')
	{
		Str[j++] = 't';
		Str[j++] = 'A';
		Str[j++] = 'd';
		Str[j++] = 'A';
	}
	Str[j] = '\0';
	Maho(Str);
}
int main()
{
	char S[100];
	int len;
	char* mahostr;
	scanf("%s", S);
	len = strlen(S);
	/*Maho(S);*/ //魔王语言测试代码
	printf("你所输入的魔王语言是:%s\n", S);
	if (match(S))
	{
		printf("匹配成功\n");
		do {
			mahostr=translate(S, len);
			/*printf("%s\n", mahostr);*/
			strcpy(S, mahostr);
			len = strlen(S);	
		}while (exist(S));
		printf("输出翻译后的结果为:");
		Maho(mahostr);
	}
	else
	{
		printf("匹配失败,输入错误\n");
		exit(0);
	}
	return 0;
}

运行结果:

多重括号:

并列括号:

 

匹配失败:

 

 

 本人第一次写文章,如果有错误请大家多多包涵并指出,大家共同进步。

也感谢大佬给我的启发。

数据结构课程设计---魔王语言解释 - 新技术 - 博客园 

  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值