#CSP202006C. Markdown 渲染器

    ID: 573 Type: Default 1000ms 512MiB Tried: 3 Accepted: 1 Difficulty: 4 Uploaded By: Tags>CSP模拟字符串处理有限状态自动机

Markdown 渲染器

时间限制: 1.0 秒

空间限制: 512 MB

题目背景

Markdown 是一种轻量级的标记语言,它可以用较为简单的符号来标记文本中的成分的意义。通过套用适当的样式,即可生成一份美观的文档。Markdown 由于其轻量级的特性,深受喜爱,并在各种方面得到了广泛的运用。本题需要你解析并渲染简化的 Markdown 文档。

题目描述

本题中,你需要按照下述要求,在终端屏幕上渲染简单的 Markdown 文档。终端屏幕,可以被视为宽度为 ww 单位,长度为无限长单位的方格,每个方格中可以被填充至多一个字符。可选地,每个方格中的字符可以带有一定的样式。在这个终端屏幕中,可以按照下列方法,渲染 Markdown 文档中的各个元素。

段落

在 Markdown 文档中,换行符将文档分为若干行。仅包含零个或多个空格的行被称为“空白行”。空白行将整个文档分割成了若干段落。例如,下面的示例文档中包含有 5 个空白行,行号分别是 2、6、7、9、10,他们将该文档分成了 3 个段落。为了能够便于你阅读,“˽”符号表示空格。“¬”符号表示换行符。

CSP¬
¬
CSP˽is¬
a˽real˽realrealrealrealreal˽¬
˽˽˽˽˽competition.¬
¬
˽˽˽¬
Come˽˽˽and˽˽˽join˽˽˽us˽˽˽¬
¬
¬

在渲染一个段落前,应该去掉每行首尾的连续空格。如果一个段落包含有多行文本,那么将各个行用一个空格连接起来,形成一个不含换行符的连续的段落文本。渲染段落时,应当首先与之前的段落或项目列表空出一行的间距,从新的一行开始连续将段落中的文本从左至右渲染在屏幕上。如果段落中的字符超出了终端的宽度,那么则应当从终端的下一行开始继续渲染其余的文本。如果余下的文本恰好是以空格字符开头,那么应该将这些连续的空格字符删除。上述文本按照这样的规则,在一个宽度为 10 的终端中渲染,其结果为:

             1
   +1---5----0+
1  |CSP       |
2  |          |
3  |CSP is a r|
4  |eal realre|
5  |alrealreal|
6  |real compe|
7  |tition.   |
8  |          |
9  |Come   and|
10 |join   us |
   +----------+

项目列表

“项目”由连续的若干行文本组成,其中第一行必须以星号和空格()开头,其余的行(如果有)为以两个空格(˽˽)开头的非空白行。“项目列表”由连续的一个或多个项目组成,其中各个项目之间没有空白行或开头不是两个空格的行。如果两个项目列表之间没有空白行或开头不是两个空格的行,那么它们应当被视为一个项目列表。例如,下列文本中,行 1 、行 3-5、行 6-7、行 9 和行 10 组成了 5 个项目,它们分别属于 3 个项目列表,因为它们被空行(行 3)、段落(行 8)分隔开来。

*˽CSP¬
˽˽˽˽¬
*˽˽˽CSP˽is¬
˽˽*˽a˽real˽˽¬
˽˽˽˽˽competition.¬
*˽¬
˽˽*˽Come!˽˽˽and˽˽˽join.¬
*Tel:¬
*˽12345¬
*˽¬

在渲染一个项目列表时,应当首先与之前的段落或项目列表空出一行的间距,从新的一行开始连续渲染每个项目。在渲染一个项目前,先把这个项目的各个文本行首的两个空格字符或星号空格字符去掉。渲染时,从新的一行开始输出空格、点号、空格(˽·˽)符号,然后按照渲染一个段落的规则,渲染该项目中的各个文本行。但是,在终端上输出一个项目的随后的行时,应当插入三个空格(˽˽˽),以使这些行与第一行对齐。特别地,如果一个项目的首行除行首的星号空格字符外,没有其它的字符,那么应当直接从项目的余下的行开始渲染该项目(例如上述文本第 6 行);如果仅有一行,那么认为这个项目渲染结束(例如上述文本第 10 行)。上述文本按照该规则,在一个宽度为 10 的终端中渲染,其结果为:

             1
   +1---5----0+
1  | · CSP    |
2  |          |
3  | · CSP is |
4  |   * a rea|
5  |   l compe|
6  |   tition.|
7  | · * Come!|
8  |   and   j|
9  |   oin.   |
10 |          |
11 |*Tel:     |
12 |          |
13 | · 12345  |
14 | ·        |
   +----------+

这里,原文本第 2 行是空白行,将前后两个项目分成了两个项目列表,因此在输出终端中,二者之间有空行分隔。原文本第 8 行,开头不是星号和空格,因此是一个单独的段落。

输入格式

从标准输入读入数据。

输入第一行包含一个数字 ww,是终端宽度。随后从第二行开始,是被渲染的文本。

输出格式

输出到标准输出。

输出一行,是一个数字,表示渲染所需终端行数。

10
CSP

CSP is
a real realrealrealrealreal 
     competition.

   
Come   and   join   us

10

样例1解释

该输入是本题第一个例子,渲染共需 10 行。

10
* CSP
    
*   CSP is
  * a real  
     competition.
* 
  * Come!   and   join.
*Tel:
* 12345
* 
14

样例2解释

该输入是本题第二个例子,渲染共需 14 行。

子任务

对于 10%10\% 的测试点,仅包含段落,且每个段落仅包含一行文本,且每行文本长度不超过 ww

对于 20%20\% 的测试点,仅包含段落,且每个段落仅包含一行文本;

对于 40%40\% 的测试点,仅包含段落;

对于 60%60\% 的测试点,仅包含段落和至多一个项目列表,且输入数据大小在 2.5 MiB 以内;

对于 80%80\% 的测试点,包含零个或多个段落和项目列表,且 w103w \leq 10^3,输入数据大小在 5 MiB 以内;

对于 100%100\% 的测试点,包含零个或多个段落和项目列表,4w1044 \leq w \leq 10^4,输入数据中仅包括 ASCII 字符,输入数据大小在 20 MiB 以内。

提示

本题需要你判断读取输入是否到达末尾。在 C 语言中 fgets 函数可以读取一段文本,直到换行符或给定的长度为止;feof 可以检查是否读到了文件末尾。在 C++ 中,std::istream::getline 函数可以读取一段文本,直到换行符或给定的长度为止;std::ios::eof 函数可以检查上一次读取是否因读取到了文件末尾而发生错误。在 Java 中,java.io.BufferedReader.readLine 函数可以一次读取一行文本,并在到达文件末尾的情况下返回 null。在 Python 中,io.TextIOBase.readline 函数可以一次读取一行文本,并在到达文件末尾的情况下返回空字符串。