问题描述
试题编号: | 201703-3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
试题名称: | Markdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
时间限制: | 1.0s | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
内存限制: | 256.0MB | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
问题描述: | 问题描述 Markdown 是一种很流行的轻量级标记语言(lightweight markup language),广泛用于撰写带格式的文档。例如以下这段文本就是用 Markdown 的语法写成的: 输入格式 输入由若干行组成,表示一个用本题规定的 Markdown 语法撰写的文档。 输出格式 输出由若干行组成,表示输入的 Markdown 文档转换成产生的 HTML 代码。 样例输入 # Hello 样例输出 <h1>Hello</h1> 评测用例规模与约定 本题的测试点满足以下条件:●本题每个测试点的输入数据所包含的行数都不超过100,每行字符的个数(包括行末换行符)都不超过100。 ●除了换行符之外,所有字符都是 ASCII 码 32 至 126 的可打印字符。 ●每行行首和行末都不会出现空格字符。 ●输入数据除了 Markdown 语法所需,内容中不会出现 `#`、`*`、`_`、`[`、`]`、`(`、`)`、`<`、`>`、`&` 这些字符。 ●所有测试点均符合题目所规定的 Markdown 语法,你的程序不需要考虑语法错误的情况。 每个测试点包含的语法规则如下表所示,其中“√”表示包含,“×”表示不包含。
提示 由于本题要将输入数据当做一个文本文件来处理,要逐行读取直到文件结束,C/C++、Java 语言的用户可以参考以下代码片段来读取输入内容。 |
本题是非常复杂的字符串处理,如果思路不对,会极大地增加编程难度。本题要处理两个主要难点是:
1、区块的结束标志字符串的输出。一个解决办法是将一个区块的内容全接收完再输出。另一个解决办法是在当前区块上不管结束字符串,当输入另一个区块时,再处理上一区块结束字符串。我使用的是第二种解决办法。
2、强调和链接的嵌套的处理。解决方法就是用递归,因为最多只有一层循环,所以只要写好强调和链接的判断处理,再递归调用一次即可。代码如下,详见注释:
#include<iostream>
#include<string>
bool list=false;
bool para=false;
using namespace std;
//处理上一区块遗留的符号
void solve()
{
//列表的结束标志
if(list)
{
cout<<"</ul>"<<endl;
list=false;
}
//段落的结束标志
if(para)
{
cout<<"</p>"<<endl;
para=false;
}
}
//检查是否有强调或者链接
string check(string a,int start,int end)
{
string text;
string link;
string res="";
int s1,s2;
int i=start;
//遍历字符串的每个字符
while(i<end)
{
switch(a[i])
{
//如果遇到了强调
case '_':{
//i++跳到_后的那个字符
i++;
//s1表示强调的内容的起始位置
s1=i;
//找到强调的内容的终止位置
for(;i<end && a[i]!='_';i++);
s2=i;
//跳到强调的第二个_的后一个字符上
i++;
//检查强调的内容是否有嵌套
res+="<em>"+check(a,s1,s2)+"</em>";
break;
}
//如果遇到了链接
case '[':{
//跳到[的下一个字符
i++;
//s1表示text的内容的起始位置
s1=i;
//找到text的内容的终止位置
for(;i<end && a[i]!=']';i++);
s2=i;
//i+=2,越过 ] 和 ( ,跳到link的内容上
i+=2;
//检查text中是否有嵌套
text=check(a,s1,s2);
//s1表示link的起始位置
s1=i;
for(;i<end&& a[i]!=')';i++);
//s2表示link的终止位置
s2=i;
//跳到链接的后一个字符上
i++;
link=a.substr(s1,s2-s1);
res+="<a href=\""+link+"\">"+text+"</a>";
break;
}
//如果遇到的是普通字符则不做处理
default:{
res+=a[i];
i++;
break;
}
}
}
return res;
}
int main()
{
string line;
while(getline(cin,line))
{
int len=line.length();
//输入遇到了换行,则证明开始了另一个区块,则要输出上一区块的结束符
if(line=="")
{
solve();
continue;
}
//输入的是标题
if(line[0]=='#')
{
int j,k,pos;
//j用来计算#的个数
for(j=0;line[j]=='#';j++);
//k用来找到标题内容的起始位置
for(k=j;line[k]==' ';k++);
//检查是否有强调或链接
string res=check(line,k,len);
printf("<h%d>",j);
cout<<res;
printf("</h%d>\n",j);
}
//列表
else if(line[0]=='*')
{
//列表的开始符号
if(!list)
{
cout<<"<ul>"<<endl;
list=true;
}
//k用来找到列表内容的起始位置
int k;
for(k=1;k<len && line[k]==' ';k++);
cout<<"<li>"<<check(line,k,len)<<"</li>"<<endl;
}
//段落
else
{
//输出上一行的换行符
if(para)
cout<<endl;
//输出行首标志
if(!para)
{
cout<<"<p>";
para=true;
}
int k;
for(k=0;k<len && line[k]==' ';k++);
cout<<check(line,k,len);
}
}
//输入结束后,检查一下是否遗漏了结束符
solve();
return 0;
}