某编程大赛中设计有一个挑战环节,选手可以查看其他选手的代码,发现错误后,提交一组测试数据将对手挑落马下。为了减小被挑战的几率,有些选手会故意将代码写得很难看懂,比如把所有回车去掉,提交所有内容都在一行的程序,令挑战者望而生畏。
为了对付这种选手,现请你编写一个代码排版程序,将写成一行的程序重新排版。当然要写一个完美的排版程序可太难了,这里只简单地要求处理C语言里的for、while、if-else这三种特殊结构,而将其他所有句子都当成顺序执行的语句处理。输出的要求如下:
默认程序起始没有缩进;每一级缩进是 2 个空格;
每行开头除了规定的缩进空格外,不输出多余的空格;
顺序执行的程序体是以分号“;”结尾的,遇到分号就换行;
在一对大括号“{”和“}”中的程序体输出时,两端的大括号单独占一行,内部程序体每行加一级缩进,即:
{
程序体
}
for的格式为:
for (条件) {
程序体
}
while的格式为:
while (条件) {
程序体
}
if-else的格式为:
if (条件) {
程序体
}
else {
程序体
}
输入格式:
输入在一行中给出不超过 331 个字符的非空字符串,以回车结束。题目保证输入的是一个语法正确、可以正常编译运行的 main 函数模块。
输出格式:
按题面要求的格式,输出排版后的程序。
输入样例:
int main() {int n, i; scanf("%d", &n);if( n>0)n++;else if (n<0) n--; else while(n<10)n++; for(i=0; i<n; i++ ){ printf("n=%d\n", n);}return 0; }
输出样例:
int main()
{
int n, i;
scanf("%d", &n);
if ( n>0) {
n++;
}
else {
if (n<0) {
n--;
}
else {
while (n<10) {
n++;
}
}
}
for (i=0; i<n; i++ ) {
printf("n=%d\n", n);
}
return 0;
}
我用了递归来做这个题,代码中的主力是 eval
函数与 evalKeyword
函数,eval函数中传入一个参数 idx ,表示它负责的代码部分的下标的开始位置,当 idx 处为左花括号时,他会一直往后处理直到遇到与开始处相匹配的右花括号。当处理的过程中遇到关键字时,就交给 evalKeyword 处理,evalKeyword 中遇到关键字后面需要处理的代码段时再调用 eval,这样两个函数互相递归调用。
evalKeyword 函数:
- 如果是 else 就直接根据后面有没有花括号两种情况交给 eval 处理
- 如果是其他关键字,就先处理后面的括号,再看有没有花括号并 eval
- 最后如果处理的关键字是 if 的话,处理完 if 就接着判断后面有没有 else,如果有的话就递归调用自己处理 else
注意以下几种情况 - 括号中可能还会有括号
if((a-b)*c>0)
- 注意处理引号
printf(";;;;");
orprintf("if(a>b)");
- 引号包括单引号
char ch=';';
orchar ch='\'';
(数据中似乎没有这种情况)
测试点1 格式错误 不知道哪里出了问题T_T
#include <bits/stdc++.h>
using namespace std;
string keyword[]={"if","else","for","while"};
string code;
void eval(int &idx,string space,bool left);
void evalKeyword(int &idx,int type,string space);
bool Space(char ch)
{
if(ch>='a'&&ch<='z') return false;
if(ch>='A'&&ch<='Z') return false;
if(ch>='0'&&ch<='9') return false;
if(ch=='_') return false;
return true;
}
// 检查 str 是否是关键字,idx为关键字字符串后面的第一个字符的下标
int check(string str,int idx)
{
int type;
for(type=0;type<4;type++) {
if(str==keyword[type]) break;
}
return Space(code[idx])?type:4;
}
// 括号、引号中的内容一定在同一行
void evalsp1(int &idx) // 处理括号
{
cout<<code[idx++];
while(code[idx]!=')') {
if(code[idx]=='(') {
evalsp1(idx);
} else {
cout<<code[idx++];
}
}
cout<<code[idx++];
}
string evalsp2(int &idx) // 处理引号
{
string res="";
res+=code[idx++];
while(code[idx]!='"') {
if(code[idx]=='\\') res+=code[idx++];
res+=code[idx++];
}
res+=code[idx++];
return res;
}
string evalsp3(int &idx) // 处理单引号
{
string res="";
res+=code[idx++];
while(code[idx]!='\'') {
if(code[idx]=='\\') res+=code[idx++];
res+=code[idx++];
}
res+=code[idx++];
return res;
}
void eraseSpace(int &idx) // 删除空格
{
while(code[idx]==' ') idx++;
}
// 处理关键字,idx为关键字字符串后的第一个字符的下标,type为关键字类型(看代码开头的keyword数组,space为当前的花括号的缩进空格)
void evalKeyword(int &idx,int type,string space)
{
cout<<space<<keyword[type]<<' ';
string now=space+" ";
eraseSpace(idx); // 删除多余空格
if(type==1) { // else的情况后面没有括号
if(code[idx]=='{') { // 后面带了花括号
eval(idx,space,false);
} else {
cout<<'{'<<'\n';
eval(idx,space,false);
cout<<space<<'}'<<'\n';
}
} else { // 其他情况后面会有括号
evalsp1(idx);
// 处理完括号之后记得加一个空格
cout<<' ';
eraseSpace(idx);
if(code[idx]=='{') {
eval(idx,space,false);
} else {
cout<<'{'<<'\n';
eval(idx,space,false);
cout<<space<<'}'<<'\n';
}
eraseSpace(idx);
if(type==0&&check(code.substr(idx,4),idx+4)==1) {
idx+=4;
evalKeyword(idx,1,space);
}
}
}
// 处理代码段,idx为代码段开头的第一个字符下标,space为缩进空格,left代表花括号(如果有的话)前面是否添加缩进
void eval(int &idx,string space,bool left)
{
string temp="",now=space+" ";
bool flag=code[idx]!='{'; // 是否处理单句
if(flag) {
bool have_keyword=false;
eraseSpace(idx); // 删去句首空格
while(code[idx]!=';') {
temp+=code[idx++];
int res=check(temp,idx);
if(res<4) { // 出现了关键字
have_keyword=true;
evalKeyword(idx,res,now);
//只有一句并且包含关键字,则不需要再找分号,直接结束
return;
}
}
if(!have_keyword) { // 输出普通语句
temp+=code[idx++];
cout<<now<<temp<<'\n';
}
} else {
if(left) cout<<space;
cout<<code[idx++]<<'\n';//将前花括号打出
while(code[idx]!='}') { // 开始找右花括号
eraseSpace(idx); // 删除多余空格
while(code[idx]!=';'&&code[idx]!='{'&&code[idx]!='}') {
if(code[idx]=='"') {
temp+=evalsp2(idx);
continue;
} else if(code[idx]=='\'') {
temp+=evalsp3(idx);
continue;
}
temp+=code[idx++];
int res=check(temp,idx);
if(res<4) {
evalKeyword(idx,res,now);
temp="";
break;
}
}
if(code[idx]==';') { // 如果只是普通语句
temp+=code[idx++];
cout<<now<<temp<<'\n';
temp="";
} else if(code[idx]=='{') { // 发现了代码块中的代码块
eval(idx,now,true);
}
}
cout<<space<<code[idx++]<<'\n';//打出右花括号
}
}
int main()
{
// ios::sync_with_stdio(false),cin.tie(0);
getline(cin,code);
int idx=0;
while(code[idx]!='{') idx++; // 找到第一个花括号的位置
cout<<"int main()\n";
eval(idx,"",true);
return 0;
}
====================================================
二编,终于过了
测试点1 int main()
中间的括号中可能有空格,需要特殊处理
修改部分如下
int main()
{
// ios::sync_with_stdio(false),cin.tie(0);
getline(cin,code);
int idx=0;
while(code[idx]!='{') idx++;
// 不能直接输出
// cout<<"int main()\n";
for(int i=0;i<idx;i++) {
cout<<code[i];
if(code[i]==')') break;
}
cout<<'\n';
eval(idx,"",true);
return 0;
}