大模拟好啊!

万一远古计算机让我写个解释器还真是得爆零了呢。

题目背景

「千歌です」(我是千歌)、「曜です」(我是曜)、「ルビィです」(我是露比)、「3人合わせて、We are CYaRon! よろしくね!」(三人在一起,我们是CYaRon!多多指教哦!)

CYaRon!的三人在学习了编程之后,决定发明一种自己的编程语言,她们称为CYaRon!语。

(ltt: 明明是我帮她们发明的。)

可是,因为洛谷评测机这时候突然爆了,ltt没有时间再帮CYaRon!的三位小姐姐写CYaRon!语的解释器了。

于是ltt就出了这样一道题,然后等着有人交了AC程序就偷走给小姐姐们。

题目描述

以下是一个典型的CYaRon! 语程序。

{ vars
chika:int
you:int
ruby:array[int, 1..2]
i:int
}
# 以上变量默认值均为0
# 变量名只可是英文字母。 # yosoro语句可以输出一个数字,随后跟一个空格。
:yosoro 2
# 输出2和一个空格(以下不再提到空格)。 # set语句可以为变量赋值。
# 运算符只支持加减号即可。
:set chika, 1
:set you, 2
:yosoro chika + you
# 上一条语句将输出3 # 以下的判断语句均使用以下的格式:
# 操作符,表达式,表达式
# 例如eq, a, 1即C语言中 a==1
# 所有操作符包括: lt: < gt: > le: <= ge: >= eq: == neq: != # 日本来的CYaRon三人没法正确地发出if这个音,因此她们就改成了ihu。
{ ihu eq, chika, 1
:set you, 3
:yosoro 1
}
# 输出1
# 以上是ihu语句,无需支持else。 # hor语句同理,
# for i=1 to you如下
{ hor i, 1, you
:yosoro i
}
# 输出1 2 3 # 如下是while和数组的使用方法。
:set i, 1
{ while le, i, 2
:yosoro i
:set ruby[i], i+1
:yosoro ruby[i]
:set i, i+1
}
# 输出1 2 2 3 # 数组不会出现嵌套,即只会有a[i]、a[i+2]而不会有类似于a[i+b[i]]这样的。 # CYaRon语的最后一行,一定是一个换行。

你的任务是写一个CYaRon!语的解释器,实现输入CYaRon!语的程序,解释之执行后输出执行结果。

输入输出格式

输入格式:

输入文件全部为CYaRon!语程序,最后一行保证是个空行。

请处理输入的时候,一直读到EOF为止。

输出格式:

该CYaRon!语程序的执行结果。

具体上,是该CYaRon!语程序所有:yosoro语句的输出。

输入输出样例

输入样例#1:

{ vars
a:int
b:int
} :set a, 1
:set b, 2
:yosoro a+b

输出样例#1:

3

说明

对数据做出以下保证:

  1. 输入数据一定是合法的CYaRon!语程序,不包含注释,代码、缩进风格 (四个空格)与上述样例相同。但不保证逗号、运算符前面的空格数量和有无一定相同
  2. 变量名在10个字符以下,仅包含小写英文,数组最大大小为1000,变量最多50个,所有表达式的运算结果,包括变量的值一定在int范围内。 (但数组可能是类似于[2001..3000]的,下标可能范围为0到1亿)
  3. 所有指令小写。
  4. 该程序一定能够在合理的时间、内存限制内执行完毕。
  5. hor语句执行过程中,循环变量、初始值、结束值不会被循环中的代码改变。
  6. 该程序最多500行

题解:

模拟即可。


以上就是题解。再有其他内容就是字符串的读入了吧。

下了三组数据才AC,不得不说#10很强。

在这个题中,函数思想很重要。因为有很多地方会调用相同的内容,甚至有时候有大篇幅相似的两部分可以合在一个函数里再加if判断。

循环的嵌套是最麻烦的内容,其次是变量求值。还好没有数组套数组(大不了多几个递归),字符串长度开得比较小。变量可以用std::map映射,然后用编号统一存起来。

对于变量求值而言,一个是单项式求值,一个是多项式(仅加减)求值,其中又要考虑是否是数组的情况。我写了一个getnext(int &i)函数,用来获取从\(i\)开始的第一个可求的式子。如果a是一个数组,那么a[7]是一个可求的式子而a不是。然后用字符串处理依次考虑正负即可。

如果经常使用gdb或某些IDE的自带debug器的话,循环嵌套的思路比较容易联想,实现起来比较麻烦废话。使用一个变量tot存储当前做到循环的第几行了,带入内层循环tot不变,循环结束时置tot为en+2(en指循环结束的}字符所在行的前一行)。

还有一个地方是循环中语句会被使用多遍,这时不能直接通过标准读入,而要把这些语句全部存起来。读一行(空格也读)用到的是gets(char *s),当读到EOF时返回NULL。

注意这个题的缩进和空格都不确定。所以需要写一个jump(),就是使\(i\)跳过前面所有的连续空格。还有其他字符的跳过额外写一点也不怎么麻烦。

感谢自己的坚持。

Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#define jump() do{while(s[i]==' ')++i;}while(0)
using namespace std;
map<string,int> mp;
int a[55][1010];
int cnt;
int st[55],len[55];
char s[1000];
int getnext(int &i)//获得从i开始接下来一段式子的值 i被置为最后一个下标+1
{
jump();
int x=0;
if(s[i]>='0'&&s[i]<='9')
{
for(;s[i]>='0'&&s[i]<='9';++i)
x=x*10+s[i]-'0';
return x;
}
string ss="";
for(;s[i]>='a'&&s[i]<='z';++i)
ss+=s[i];
int t=mp[ss];
if(len[t]==1)
return a[t][0];
jump();
++i;
int ans=a[t][getnext(i)-st[t]];
++i;
return ans;
}
int getstring(int &i)//获得从i开始的一个多项式的值
{
int ans=0,flag=1;
jump();
while((s[i]>='0'&&s[i]<='9')||(s[i]>='a'&&s[i]<='z')||s[i]=='+'||s[i]=='-')
{
flag=(s[i]=='-')?-1:1;
while(s[i]==' '||s[i]=='+'||s[i]=='-')
++i;
ans+=flag*getnext(i);
jump();
}
return ans;
}
void Set()//赋值
{
int i=0;
jump();
i+=4;
jump();
string ss="";
for(;s[i]>='a'&&s[i]<='z';++i)
ss+=s[i];
int t=mp[ss],u=0,v=0;
if(len[t]==1)
{
while(s[i]==' '||s[i]==',')
++i;
a[t][0]=getstring(i);
}
else
{
++i;
u=getstring(i);
//i是']'
++i;
while(s[i]==' '||s[i]==',')
++i;
a[t][u-st[t]]=getstring(i);
}
}
void print()
{
int i=0;
jump();
i+=7;
jump();
while(s[i]==' '||s[i]==',')
++i;
printf("%d ",getstring(i));
} void qaq();
bool ihu(int ty);
void hor();
void whi(); char F[500][100];
int tot=0; bool isend()//是否结尾 判断ihu、hor和while是否结束
{
if(!tot)
gets(s);
else
{
int i=0;
for(;F[tot][i]!='\0';++i)
s[i]=F[tot][i];
s[i]='\0';
++tot;
}
int i=0;
jump();
return (s[i]=='}');
} int main()
{
gets(s);
int i=1,flag=0;
jump();
if(s[i]=='v')//如果没有定义就要跳过
{
gets(s);
while(s[0]!='}')
{
string ss="";
int i=0;
jump();
for(;s[i]>='a'&&s[i]<='z';++i)
ss+=s[i];
mp[ss]=++cnt;
jump();
if(s[i+1]=='i')
st[cnt]=len[cnt]=1;
else
{
i+=10;
while(s[i]==' '||s[i]==',')
++i;
//逗号前后的空格
st[cnt]=getnext(i);
while(s[i]==' '||s[i]=='.')
++i;
len[cnt]=getnext(i);
len[cnt]-=st[cnt]-1;
}
gets(s);
}
}
else
flag=1; while(flag||gets(s)!=NULL)
{
flag=0;
if(!strlen(s))
continue;
qaq();
tot=0;
}
return 0;
}
void qaq()//执行语句
{
int i=0;
jump();
if(s[i]=='{')
{
++i;
jump();
if(s[i]=='i')
ihu(0);
else if(s[i]=='w')
whi();
else
hor();
}
else if(s[i+1]=='s')
Set();
else
print();
} bool ihu(int ty)
{
int i=0;
jump();
++i;
jump();
i+=3+ty*2;
jump();
char tmp[2]={s[i],s[i+1]};
i+=2;
if(tmp[0]=='n')
++i;
while(s[i]==' '||s[i]==',')
++i;
int x=getstring(i);
while(s[i]==' '||s[i]==',')
++i;
int y=getstring(i);
bool sat;
if(tmp[0]=='e')
sat=(x==y);
else if(tmp[0]=='l')
{
if(tmp[1]=='t')
sat=(x<y);
else
sat=(x<=y);
}
else if(tmp[0]=='n')
sat=(x!=y);
else
{
if(tmp[1]=='t')
sat=(x>y);
else
sat=(x>=y);
}
if(ty)
return sat;
int br=1;
if(!sat)
while(1)
{
if(isend()&&br==1)
return 0;
i=0;
jump();
if(s[i]=='{')
++br;
if(s[i]=='}')
--br;
}
else
{
while(1)
{
if(isend()&&br==1)
return 0;
if(!strlen(s))
continue;
qaq();
}
}
}
void hor()
{
int i=0;
jump();
++i;
jump();
i+=3;
jump();
string ss="";
for(;s[i]>='a'&&s[i]<='z';++i)
ss+=s[i];
int t=mp[ss];
int *ii,u;//用指针引用值更加方便
if(len[t]==1)
ii=&a[t][0];
else
{
++i;
u=getstring(i);
ii=&a[t][u-st[t]];
}
while(s[i]==' '||s[i]==',')
++i;
int from=getstring(i);
while(s[i]==' '||s[i]==',')
++i;
int to=getstring(i);
int st=1,en=0,l=0;
if(tot)
st=tot;
int br=1;
while(1)
{
if(isend()&&br==1)//注意不要找到别人的右括号了 下同
{
if(tot)
en=tot-2;//en是右括号上面一行相对st(1)的编号
else
en=l;
break;
}
if(!strlen(s))//注意空串如果跳过会方便一些
continue;
++l;
i=0;
jump();
if(s[i]=='{')
++br;
if(s[i]=='}')
--br;
if(!tot)
{
i=0;
for(;s[i]!='\0';++i)
F[l][i]=s[i];
F[l][i]='\0';
}
}
for(*ii=from;*ii<=to;++(*ii))
{
tot=st;
while(tot<=en)
{
i=0;
for(;F[tot][i]!='\0';++i)
s[i]=F[tot][i];
s[i]='\0';
++tot;
qaq();
}
}
tot=en+2;
} void whi()
{
int i=0;
char p[1000];
for(;s[i]!='\0';++i)
p[i]=s[i];
p[i]='\0';
int st=0,en=0,l=0;
if(tot)//最外层不需要处理这里
{
st=tot-1;
++tot;
} int br=1;
while(1)
{
if(isend()&&br==1)
{
if(tot)
en=tot-2;
else
en=l;
break;
}
if(!strlen(s))
continue; i=0;
++l;
jump();
if(s[i]=='{')
++br;
if(s[i]=='}')
--br;
if(!tot)
{
i=0;
for(;s[i]!='\0';++i)
F[l][i]=s[i];
F[l][i]='\0';
}
} while(1)
{
tot=st+1;
i=0;
for(;p[i]!='\0';++i)
s[i]=p[i];
s[i]='\0';
if(!ihu(1))
{
tot=en+2;
return;
} while(tot<=en)
{
i=0;
for(;F[tot][i]!='\0';++i)
s[i]=F[tot][i];
s[i]='\0';
++tot;
qaq();
}
}
}

洛谷 P3695 CYaRon!语 题解 【模拟】【字符串】的更多相关文章

  1. 洛谷P5020 货币系统 题解 模拟

    题目链接:https://www.luogu.org/problem/P5020 这道题目是一道模拟题,但是又有一点多重背包的思想在里面. 首先我们定义一个 vis[i] 来表示和为 i 的情况在之前 ...

  2. 洛谷P5019 铺设道路 题解 模拟/贪心基础题

    题目链接:https://www.luogu.org/problemnew/show/P5019 这道题目是一道模拟题,但是它有一点贪心的思想. 我们假设当前最大的深度是 \(d\) ,那么我们需要把 ...

  3. 洛谷 P5594 【XR-4】模拟赛

    洛谷 P5594 [XR-4]模拟赛 洛谷传送门 题目描述 X 校正在进行 CSP 前的校内集训. 一共有 nn 名 OIer 参与这次集训,教练为他们精心准备了 mm 套模拟赛题. 然而,每名 OI ...

  4. 洛谷P2832 行路难 分析+题解代码【玄学最短路】

    洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座 ...

  5. 【洛谷P3960】列队题解

    [洛谷P3960]列队题解 题目链接 题意: Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 n×m ...

  6. 洛谷P2312 解方程题解

    洛谷P2312 解方程题解 题目描述 已知多项式方程: \[a_0+a_1x+a_2x^2+\cdots+a_nx^n=0\] 求这个方程在 \([1,m]\) 内的整数解(\(n\) 和 \(m\) ...

  7. 洛谷P1577 切绳子题解

    洛谷P1577 切绳子题解 题目描述 有N条绳子,它们的长度分别为Li.如果从它们中切割出K条长度相同的 绳子,这K条绳子每条最长能有多长?答案保留到小数点后2位(直接舍掉2为后的小数). 输入输出格 ...

  8. 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)

    洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...

  9. 洛谷 P1220 关路灯 题解

    Description 有 $n$ 盏路灯,每盏路灯有坐标(单位 $m$)和功率(单位 $J$).从第 $c$ 盏路灯开始,可以向左或向右关闭路灯.速度是 $1m/s$.求所有路灯的最少耗电.输入保证 ...

随机推荐

  1. mysql外键约束总结

    总结三种MySQL外键约束方式 如果表A的主关键字是表B中的字段,则该字段称为表B的外键,表A称为主表,表B称为从表.外键是用来实现参照完整性的,不同的外键约束方式将可以使两张表紧密的结合起来,特别是 ...

  2. 455. Assign Cookies 满足欲望 分配饼干

    [抄题]: Assume you are an awesome parent and want to give your children some cookies. But, you should ...

  3. opennebula 镜像池

    { "IMAGE_POOL": { "IMAGE": [ { ", ", ", "TEMPLATE": { & ...

  4. Docker 学习笔记_安装和使用MongoDB

    一.准备 1.宿主机OS:Win10 64 2.虚拟机OS:Ubuntu18.04 3.账号:docker 二.安装 1.搜索MongoDB镜像                            ...

  5. keystone部署及操作

    目录 一  版本信息 二  部署keystone 三  keystone操作 四  验证 五  创建脚本 六  keystone使用套路总结 一.版本信息 官网http://docs.openstac ...

  6. Smarty3——复合变量修饰器输

    你可以联合使用多个修饰器. 它们会按复合的顺序来作用于变量,从左到右. 它们必须以| (竖线)进行分隔,以‘:’号设置参数 {$articleTitle} {$articleTitle|upper|s ...

  7. 使用 Vue.component

    引入 vue.js. HTML <div id="app"></div> CSS .greeting { padding: 3rem 1.5rem; bac ...

  8. IDEA+DevTools实现热部署功能

      开发IDE: Intellij IDEA 2018.1 SpringBoot:1.5.9.RELEASE 热部署 大家都知道在项目开发过程中,常常会改动页面数据或者修改数据结构,为了显示改动效果, ...

  9. Java集合框架---重构设计

    面向接口编程: 接口类型 变量 =new 实现类(); List  list=new ArrayList(); --------------------------------------- List ...

  10. 很棒的bootstrap学习网站

    http://www.w3cschool.cc/bootstrap/bootstrap-tutorial.html