Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / algorithm

ZigZag-Conversion-Problem

4.74/5 (11 votes)
4 Sep 2015CPOL2 min read 18K   86  
Usage of Linq and anonymous Methods help keep code briefly

Download ZigZagConversion.zip (8 KB)

This article is an alternative to Graph is everywhere by Eric Z. Maybe you'd like to refer it, since he provides a very different Solution-Concept.

Problem

The ZigZag-Conversion-Problem is a fancy programmer-riddle:

see the string "PAYPALISHIRING" written in a zigzag-pattern on a given number of rows, eg like this (3 rows):

P   A   H   N
 A P L S I I G
  Y   I   R

Reading it in horizontal lines results to: "PAHNAPLSIIGYIR"
Now write a function for such conversion.

Solution-Concept

As my Original said, traditionally this is solved by searching a pattern among the index of the letters on the same row. And as a traditionallist this is exactly what I did ;-)

Compare zigzags with several row-count, and for each letter in the zigzag note down the stepsize to reach the next letter in the horizontal line:

(3 Rows)
P   A   H   N
 A P L S I I G
  Y   I   R
steps: 4,2,4,2,...

(4 Rows)
P     I     N
 A   L S   I G
  Y A   H R
   P     I
steps: 6,4,2,6,4,2,...

These two samples are actually sufficiant to recognize the pattern: Its a cyclic repetition of a small sequence.
The cycles size equals row-count - 1.

Code

C#
static string ZigZagConvert(string input, int nRows) {
    var cycle = (nRows - 1).Times().From(nRows * 2 - 2).Step(-2); // nRows:3=>{4,2}, 4=>{6,4,2} ...
    var infinite = int.MaxValue.TimesRepeat(cycle).SelectMany(ccl => ccl);
    var map = infinite.Take(input.Length).ToArray();
    var result = new List<int>();
    for (var line = 0; line < nRows; line++) {
        for (var index = line; index < input.Length; index += map[index]) result.Add(index);
    }
    return new string(result.Select(i => input[i]).ToArray());
}

Let me try to explain the non-trivials of those few code-lines:

  • var cycle = (nRows - 1).Times().From(nRows * 2 - 2).Step(-2);
    Defines the small sequence, mentioned before
    Note the use of my fancy int.Times() - Extension - normally the line would be expressed as:
    var cycle = Enumerable.Range(1, nRows - 1).Select(i => (nRows - i) * 2);
     
  • var infinite = int.MaxValue.TimesRepeat(cycle).SelectMany(ccl => ccl);
    An (nearby) endless repetition of the cyclic sequence, with flattend hierarchy (achived by .SelectMany())
    Again one of my int-Extensions in use - you may consider it as:
    var infinite = Enumerable.Repeat(cycle, int.MaxValue).SelectMany(ccl => ccl);
     
  • var map = infinite.Take(input.Length).ToArray();
    Create an arrray which maps each input-letter-index to a stepsize, where to find the next letter in horizontal line.
     
  •  (skip two trivial lines)
     
  • for (var index = line; index < input.Length; index += map[index]) result.Add(index);
    This code-line loops the horizontal-line-letters from one to the next - very similar to a Graph-Traversal:
    input-letter-indicees define nodes, and the mapping-array defines edges.

Using the code

C#
static void Main() {
    for (var i = 2; i < 6; i++) Console.WriteLine(ZigZagConvert("PAYPALISHIRING", i));
    Console.ReadKey();
}

Outputs four variants of zigzag-converted "PAYPALISHIRING"

ZigZag Output
P Y A I H R N
 A P L S I I G
PYAIHRNAPLSIIG
P   A   H   N
 A P L S I I G
  Y   I   R
PAHNAPLSIIGYIR
P     I     N
 A   L S   I G
  Y A   H R
   P     I
PINALSIGYAHRPI
P       H
 A     S I
  Y   I   R
   P L     I G
    A       N
PHASIYIRPLIGAN

Points of Interest

As so often just looking at several input/output - samples leads to a convenient solution.

Extension-Functions, especially the Linq-stuff, bring up very brief code, quite intuitive to understand.

By the way a small Introducion to some of my int.Times()-Extensions (contained in the attached Source-File). I often need sequences, and i prefer to have them available as Extension to get rid of the little circuitous use of static Enumerable.Range().

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)