Print Strings Right Justified – in a Rectangular Frame


Task: Write a function that takes a list of strings and prints them, one per line, right justified, in a rectangular frame. For example the list [“Hello”, “World”, “in”, “a”, “frame”] gets printed as:

*********
* Hello *
* World *
*    in *
*     a *
* frame *
*********

This task is often used as a online screening interview. It is not difficult as you can only print a word in a line. The idea is to get the maximum length of word first. Then we know the width of the frame. The height of the frame is certainly equal to the number of words plus two.

And, you would need a function to print a few stars. You might quickly write this:

1
2
3
4
5
6
inline void printStars(int count) {
    for (int i = 0; i < count; i ++) {
        cout << "*";
    }
    cout << endl;
}
inline void printStars(int count) {
    for (int i = 0; i < count; i ++) {
        cout << "*";
    }
    cout << endl;
}

It is intuitive, however, with Modern C++, you should use the string constructor, to construct a string that contains only the repeative characters (e.g. string.repeat). We can use the following to print a few stars.

1
2
3
inline void printStars(int count) {
    cout << string(count, '*') << endl;
}
inline void printStars(int count) {
    cout << string(count, '*') << endl;
}

And following to print a few spaces.

1
2
3
inline void printSpaces(int count) {
    cout << string(count, ' ');
}
inline void printSpaces(int count) {
    cout << string(count, ' ');
}

We can pass the list of strings (using vector of string) and a maximum width into the print function. If the longest word exceeds the maximum length, we can throw exception – which is a good way to allow a system spotting errors sooner rather than hiding/postponding the errors (throw as much as possible and catch rarely)

Then, for each line, it is just a simple math problem to count the number of leading white spaces needed to print.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdexcept>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
 
void justify(const vector<string> &words, int maxwidth) {
    int len = 0;
    for (const auto &n: words) {
        len = max(len, (int)n.size());
    }
    if (len > maxwidth) {
         throw std::invalid_argument( "max width exceeded." );
    }
    printStars(len + 4);
    for (const auto &n: words) {
        int space_needed = len - n.size() + 1;
        cout << "* ";
        printSpaces(space_needed);
        cout << n << " *" << endl;
    }
    printStars(len + 4);
}
 
int main() {
    justify({"Hello", "World", "in", "a", "frame"}, 80);
    return 0;
}
#include <stdexcept>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

void justify(const vector<string> &words, int maxwidth) {
    int len = 0;
    for (const auto &n: words) {
        len = max(len, (int)n.size());
    }
    if (len > maxwidth) {
         throw std::invalid_argument( "max width exceeded." );
    }
    printStars(len + 4);
    for (const auto &n: words) {
        int space_needed = len - n.size() + 1;
        cout << "* ";
        printSpaces(space_needed);
        cout << n << " *" << endl;
    }
    printStars(len + 4);
}

int main() {
	justify({"Hello", "World", "in", "a", "frame"}, 80);
	return 0;
}

If we need to test the function, it ‘s better to change the function to return string (instead of void and print) – that we can unit test it. In C++, the string concatenation is not efficient, thus you would also need to pre-allocate the string buffer, similar to StringBuffer in Java.

Another implementation of Right-Justified in a Frame

In order to be better unit-tested, we can change the return type to return lines of output in a vector of string. And we can get the maximum width by using std::accumulate.

Get a line of stars in string:

1
2
3
inline string lineOfStars(int n) {
  return string(n, '*');
}
inline string lineOfStars(int n) {
  return string(n, '*');
}

A helpful function to Pad a string with spaces to the left to in order to make a total width.

1
2
3
4
5
6
7
inline string padLeft(string str, int width) {
  int n = static_cast<int>(str.size());
  if (n >= width) {
     return str;
  }
  return string(width - n, ' ') + str;
}
inline string padLeft(string str, int width) {
  int n = static_cast<int>(str.size());
  if (n >= width) {
     return str;
  }
  return string(width - n, ' ') + str;
}

We can then return the justify function a vector of string:

1
2
3
4
5
6
7
8
9
10
11
12
13
vector<string> rightJustified(const vector<string> &words) {
  int n = static_cast<int>(words.size());
  int maxWidth = std::accumulate(begin(words), end(words), 0, [](auto &a, auto &b) {
    return max(a, (int)b.size());
  });
  int recWidth = maxWidth + 4;
  vector<string> res(n + 2);
  res.back() = res[0] = lineOfStars(recWidth);
  for (int i = 0; i < n; ++ i) {
    res[i] = "* " + padLeft(words[i], maxWidth) + " *";
  }
  return res;
}
vector<string> rightJustified(const vector<string> &words) {
  int n = static_cast<int>(words.size());
  int maxWidth = std::accumulate(begin(words), end(words), 0, [](auto &a, auto &b) {
    return max(a, (int)b.size());
  });
  int recWidth = maxWidth + 4;
  vector<string> res(n + 2);
  res.back() = res[0] = lineOfStars(recWidth);
  for (int i = 0; i < n; ++ i) {
    res[i] = "* " + padLeft(words[i], maxWidth) + " *";
  }
  return res;
}

We can throw exception e.g. when there is empty line (throw often and catch rarely). Alternatively, we can return the function as a boolean to indicate if it succeeds, and pass the result (vector of string) as a by-reference variable.

--EOF (The Ultimate Computing & Technology Blog) --

GD Star Rating
loading...
762 words
Last Post: WordPress Membership Plugin
Next Post: Algorithms to Find the Missing Element in Sorted Array

The Permanent URL is: Print Strings Right Justified – in a Rectangular Frame

Leave a Reply