CF70B Text Messaging 题解
题意简介
有以下递归定义:
消息 = 句子 or 句子 空格 消息
句子 = 单词 空格 句子 or 单词 结尾
结尾 = {'.', '?', '!'}
单词 = 字母 or 字母 单词
字母 = {'a'..'z', 'A'..'Z'}
空格 = ' '
(结尾为句子结尾。)
现在有一个字符串 $s$,问最少将 $s$ 拆分成多少个长度不超过 $n$ 的消息发出。
解法
思路
首先 $s$ 中的句子明显不可以调换顺序,所以只有相邻的句子能合并成一个消息。
具体地说,如果两个相邻的句子或消息 $a$ 和 $b$,若 $|a|+1+|b|\le n$,则可合并为 $a+b$。当然,合并后的消息也可以再次合并。
注意:两个长度相加要再加 $1$,因为消息的定义之一为 消息 = 句子 空格 消息
,空格也占一个长度。
所有句子的内容在本题中并不重要,只需知道它们的长度即可。
算法流程
- 首先读入 $n,s$,处理出 $s$ 中每个句子的长度。
- 将第一个句子的长度存入 vector 中。
- 若还有句子,则尝试合并。
- 如果两个相邻的句子或消息的长度相加再加 $1$ 小于等于 $n$,则合并。
- 继续合并。
代码
有注释。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define c s[i]
int main()
{
int n;
scanf("%d\n",&n);//一定要读入回车,否则 getline 会出错
string s;
getline(cin,s);//读入一整行
int len=0,siz=s.size();//len:当前句子长度 siz:整个字符串长度
vector<int> mes;
for(int i=0;i<siz;i++)
{
if(c=='.'||c=='?'||c=='!')//如果句子结束
{
len++;
if(len>n)//如果超限,输出不可能
{
puts("Impossible");
return 0;
}
if(!mes.empty()&&mes.back()+len+1<=n)//能合并
{
mes.back()+=(len+1);//就合并
}
else
{
mes.push_back(len);//创建新消息
}
// printf("%d\n",len);
i++;//跳过空格
len=0;
}
else
{
len++;//否则长度加 1
}
}
printf("%d\n",mes.size());//输出消息数量
return 0;
}