/** * Zig-zag Cipher * Problem 2, AIC 2004 (Senior) * C# Sample Solution */ /** * This solution deciphers the message by: * - first determining the length of the original message, textLength; * - encrypting the integers 0 .. textLength-1 according to the scheme in the * question. * - reading the numbers left-to-right, top-to-bottom from the step above to * generate a table that links index in the encrypted text to an index in * the plain text. * - use this table to decrypt the message. * * For example, to decrypt COPATGAHRRY in 3 rows, we consider the 12 integers * 0, 1, 2, ... 11. Encrypting this into a 2-D array, dummyText, makes the * array look like: * * 0 5 6 11 * 1 4 7 10 * 2 3 8 9 * * We can then generate a table, decryptMap, that matches the position in the * encrypted text to their index in the original text: * * i 0 1 2 3 4 5 6 7 8 9 10 11 * decryptMap[i] 0 5 6 11 1 4 7 10 2 3 8 9 * * We can then read the original message by printing out the characters in the * order given by the table. * */ using System; using System.IO; using System.Text; public class ZigZagCipher { public const int MAXMESSAGE = 10000; public const int MAXROWS = 100; // Input and output files private static StreamReader sr; private static StreamWriter sw; // decryptMap[i] = ciphertext index for plaintext[i]. private static int[] decryptMap = new int[MAXMESSAGE]; private static int[,] dummyText = new int[MAXROWS, MAXMESSAGE]; private static int[] rowLen = new int[MAXROWS]; private static int rows; private static String ciphertext; /** * Read the next token from the input file. * Tokens are separated by whitespace, i.e., spaces, tabs and newlines. * If end-of-file is reached then an empty string is returned. */ private static String readToken() { StringBuilder ans = new StringBuilder(); int next; // Skip any initial whitespace. next = sr.Read(); while (next >= 0 && char.IsWhiteSpace((char)next)) next = sr.Read(); // Read the following token. while (next >= 0 && ! char.IsWhiteSpace((char)next)) { ans.Append((char)next); next = sr.Read(); } return ans.ToString(); } /** * The main program. */ public static void Main(string[] args) { // Open the I/O files. sr = new StreamReader("zigin.txt"); sw = new StreamWriter("zigout.txt"); // Read the number of rows the deciphered text will fill. rows = int.Parse(readToken()); // Clear the rowLen array - assume all rows are empty. for (int i = 0; i < rows; i++) rowLen[i] = 0; // Read in the first line of text. StringBuilder ciphertextBuffer = new StringBuilder(); String text; text = readToken(); // If the current line of text is a "#", then we have the entire input. while (!text.Equals("#")) { // We have a string which is not just a # // Append it to the existing string. ciphertextBuffer.Append(text); // Read in the next line. text = readToken(); } ciphertext = ciphertextBuffer.ToString(); sr.Close(); // We know the length of the encrypted text and the number of rows that it // fills. // // Fill in the dummyText array with the encrypted version of the integers // 0 .. textLength-1. int currRow = 0; // Starting at row 0. int currDir = 1; // Heading down to begin with. for (int i = 0; i < ciphertext.Length; i++) { // Mark this position in the dummy array. dummyText[currRow, rowLen[currRow]] = i; rowLen[currRow]++; // Move to the next row for the next character. currRow += currDir; // If we are at the top or the bottom, change direction. if (currRow == rows - 1) currDir = -1; else if (currRow == 0) currDir = 1; } // Construct the decryptMap array from the dummyText. int pos = 0; for (currRow = 0; currRow < rows; currRow++) for (int i = 0; i < rowLen[currRow]; i++) decryptMap[dummyText[currRow, i]] = pos++; // We now know the order to print out the letters from the plain text. for (pos = 0; pos < ciphertext.Length; pos++) sw.Write(ciphertext[decryptMap[pos]]); sw.WriteLine(); sw.Close(); } }