问题描述:
魔王的语言是由以下两种形式的规则由人的语言逐步抽象上去的。
(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;
}
运行结果:
多重括号:
并列括号:
匹配失败:
本人第一次写文章,如果有错误请大家多多包涵并指出,大家共同进步。
也感谢大佬给我的启发。