String Zigzag Conversion Overview

The string 0123456789 is written in a zigzag pattern (following the path going all the way down, then diagonally moving towards the top right) using a given number of rows. Like this example in the case of 3 rows.

0   4   8
1 3 5 7 9
2   6


This string is then read line by line and converted into a new string. In this case this new string from the zigzag will be 0481357926.

I'd like to understand any optimizations I can make using C++17 features, edge cases I may gave missed, possible overflows errors, etc.

#include &lt;cmath&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;

class Solution {
    public:
        std::string convert(const std::string&amp; s, const int numRows) {
            if (numRows == 1) { return s; }
            const int size = s.size();
            std::string converted = "";
            const int num  = numRows + (numRows - 2);
            int row = 0;
            int i   = 0;
            int sub = 0;
            bool useDiff = true;
            while (row &lt; numRows) {
                converted += s[row];
                if (row == 0 || row == numRows - 1) {
                    i = row + num;
                    while (i &lt; size) {
                        std::string str(1, s[i]);
                        converted.append(str);
                        i += num;
                    }
                } else {
                    int diff = std::abs(num - sub);
                    i = diff;
                    while (i &lt; size &amp;&amp; row + i &lt; size) {
                        if (useDiff) {
                            std::string str(1, s[row + i]);
                            converted.append(str);
                            useDiff = false;
                            i += sub;
                        } else {
                            std::string str(1, s[row + i]);
                            converted.append(str);
                            useDiff = true;
                            i += diff;
                        }
                    }
                }
                useDiff = true;
                sub += 2;
                row++;
            }

            return converted;
        }
};

int main()
{
    Solution solution;
    std::string s = "0123456789";
    int numRows = 3;
    std::cout &lt;&lt; s &lt;&lt; '\n';
    s = solution.convert(s, numRows);
    std::cout &lt;&lt; s &lt;&lt; '\n';
}
Don't use an alibi-class. It doesn't make the code any more OO, even in the cases it would be desirable, it just adds to the clutter.
Solution and convert are such generic names. Naming is hard, but good names really help.
You know an int need not be positive? An unsigned type may be more appropriate for a count, but even then you should handle zero.
Only if you need a null-terminated string should you consider a parameter of type std::string const&amp;. In all other cases, using std::string_view is more efficient.
While creating a temporary string of length 1 probably doesn't cause dynamic allocation due to small-string-optimisation (or allocation ellision), doing so just to add a character to the result-string is cumbersome and potentially quite inefficient (in theory, the compiler could optimise it all out). Just append it directly.
If you assign directly, diff is gone.
useDiff doesn't make a difference, so remove it.
Consider adding additional test-cases. For that, you should rewrite main().


See the modified code live on coliru:

#include &lt;string&gt;
#include &lt;string_view&gt;
#include &lt;stdexcept&gt;

auto zigzag(std::string_view s, std::size_t cRows) {
    if (!cRows)
        throw std::domain_error("Must output at least one row.");
    if (!cRows || cRows &gt;= s.size())
        return std::string(s);
    std::string r;
    r.reserve(s.size());
    const auto period = 2 * cRows - 2;
    for (auto row = 0 * cRows; row &lt; cRows; ++row) {
        auto delta = 2 * row;
        auto delta2 = period - delta;
        if (!delta) delta = period;
        if (!delta2) delta2 = period;
        for (auto i = row; i &lt; s.size(); i += delta) {
            r += s[i];
            std::swap(delta, delta2);
        }
    }
    return r;
}

#include &lt;iostream&gt;

struct test {
    std::string_view in;
    std::size_t n;
    std::string_view out;
};

int main() {
    constexpr test cases[]{
        "0123456789", 3, "0481357926",
        "", 1, "",
        "0123456789", 10, "0123456789",
        "0123456789", 2, "0246813579",
    };
    for (auto&amp;&amp; x : cases) {
        std::cout &lt;&lt; "zigzag(\"" &lt;&lt; x.in &lt;&lt; "\", " &lt;&lt; x.n &lt;&lt; ") == \"" &lt;&lt; x.out;
        auto r = zigzag(x.in, x.n);
        if (r == x.out)
            std::cout &lt;&lt; "\": PASSED\n";
        else
            std::cout &lt;&lt; "\": FAILED, got \"" &lt;&lt; r &lt;&lt; "\"\n";
    }
}