UVA1593 , ACM ICPC 2010 NEERC (Northeastern European Regional Contest)
https://vjudge.net/problem/UVA-1593
书上的习题5-1,按照个人的惯例,书上有的题目(主要是由简短中文翻译)就不贴问题描述了。可以在上面的链接中进入Vjudge查看详细的问题描述。作者把问题放在第五章C++和STL中,主要是要会灵活使用STL来提高解题效率。
题目要求每个单词左对齐,且要求单词之间至少空一格。其实不难看出,问题的关键在于确定每一列的列宽度。确定列宽度也不难思考,只要遍历所有数据,取最长的那个单词的长度,再加上1,即可得到该列的长度。按照这个思路,想在一次循环中边输入边处理是不大可能的,因此我使用了一个数组nlength来存放每一列最长的字符串的长度,并保存每一行的输入。第一次循环相当于预处理步骤,整个复杂度是输入的行数的线性函数关系$O(n)$。从而在第二次循环时,只需要根据已经确定好的表(维度:maxline * nlengthcount),来逐行打印输出即可(也是$O(n)$复杂度)。
对于输出时,如何从一行字符串的输入分割出单词的列,可以使用stringstream类的>>运算符。缺点是开销较大;优点就是不用手写分割函数,出错的几率小。具体什么时候用这些C++的库,我也一时拿不准,只能说凭感觉了。
除此之外,输出时我也定义了一个string临时变量,在输出时,按照行优先规则先循环mlinetext,然后循环nlength,先输出单词,然后填补需要的空格,接着转向下一列,直到ss(stringstream)碰到结束为止(或者也可以在表上做标记,因为单词长度不可能为0,表可以初始化为0,因此读到0即该行结束)。注意每一行后可能会有多余的空格,而这时题目不允许的。因此可以使用string类本身的pop_back()函数,逐个剔除空格。虽然这样删除空格效率可能有点低,但是容易实现,且对本题的输入数据来说已经足够。
最后附上我的C++实现:(freopen忘记删了,注意一下。)
1 #include<iostream> 2 #include<algorithm> 3 #include<sstream> 4 using namespace std; 5 string mlinetext[1000]; 6 size_t nlength[200]; 7 int nlengthcount; 8 int main() 9 { 10 using namespace std; 11 freopen("input.txt", "r", stdin); 12 char buf[201]; 13 string s; 14 int maxline = 0;; 15 while (cin.getline(buf, 200)) 16 { 17 mlinetext[maxline] = buf; 18 stringstream ss(buf); 19 int i = 0; 20 while (ss >> s) 21 { 22 nlength[i] = max(s.length(), nlength[i]); 23 i++; 24 nlengthcount = max(i, nlengthcount); 25 } 26 maxline++; 27 } 28 string tmpline; 29 for (int n = 0; n < maxline; n++) 30 { 31 tmpline.clear(); 32 stringstream ss(mlinetext[n]); 33 int m = 0; 34 while (ss >> s && m < nlengthcount) 35 { 36 tmpline.append(s); 37 if (nlength[m] == s.length()) 38 { 39 tmpline.push_back(' '); 40 } 41 else 42 { 43 tmpline.append(nlength[m] - s.length() + 1, ' '); 44 } 45 m++; 46 } 47 while (tmpline.back() == ' ') 48 tmpline.pop_back(); 49 cout << tmpline << endl; 50 } 51 return 0; 52 }