编写一个函数/方法,它接受2个参数、一个字符串和轨道数,并返回ENCODED字符串。
编写第二个函数/方法,它接受2个参数、一个编码字符串和轨道数,并返回DECODED字符串。
然后使用围栏密码对其进行解码。
这种密码用于通过将每个字符沿着一组“竖状轨道”依次放在对角线上来对字符串进行编码。首先开始对角向下移动。当你到达底部时,反转方向,对角向上移动,直到你到达顶部轨道。继续,直到到达字符串的末尾。然后从左到右读取每个“尾号”以导出编码字符串。
例如,字符串“WEAREDISCOVEREDFLEATONCE”可以在三轨系统中表示如下:
W E C R L T E
E R D S O E E F E A O C
A I V D E N
编码的字符串将是:
WECRLTEERDSOEEFEAOCAIVDEN
对于编码和解码,假设尾号的数量>=2,并且传递空字符串将返回空字符串。
请注意,为了简单起见,上面的示例排除了标点符号和空格。然而,也有一些测试包括标点符号。不要过滤掉标点符号,因为它们是字符串的一部分。
算法实现:
1 using System; 2 using System.Linq; 3 4 public class RailFenceCipher 5 { 6 public static string Encode(string s, int n) 7 { 8 var mod = (n - 1) * 2; 9 return string.Concat(s.Select((c, i) => new { c, i }) 10 .OrderBy(a => Math.Min(a.i % mod, mod - a.i % mod)) 11 .Select(a => a.c)); 12 } 13 14 public static string Decode(string s, int n) 15 { 16 var mod = (n - 1) * 2; 17 var pattern = Enumerable.Range(0, s.Length) 18 .OrderBy(i => Math.Min(i % mod, mod - i % mod)); 19 return string.Concat(s.Zip(pattern, (c, i) => new { c, i }) 20 .OrderBy(a => a.i).Select(a => a.c)); 21 } 22 }
测试用例:
1 using NUnit.Framework; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 public class SolutionTest 6 { 7 [Test] 8 public void BasicTests() 9 { 10 string[][] config = 11 { 12 new[] { "Hello, World!", "Hoo!el,Wrdl l" }, // encode 13 new[] { "Hello, World!", "Hlo ol!el,Wrd" }, // encode 14 new[] { "Hello, World!", "H !e,Wdloollr" }, // encode 15 new[] { "H !e,Wdloollr", "Hello, World!" }, // decode 16 new[] { "", "" }, // encode 17 new[] { "", "" }, // decode 18 new[] { "WEAREDISCOVEREDFLEEATONCE", "WECRLTEERDSOEEFEAOCAIVDEN" }, // encode 19 new[] { "WECRLTEERDSOEEFEAOCAIVDEN", "WEAREDISCOVEREDFLEEATONCE" }, // decode 20 new[] { "WEAREDISCOVEREDFLEEATONCE", "WIREEEDSEEEACAECVDLTNROFO" }, // encode 21 new[] { "WIREEEDSEEEACAECVDLTNROFO", "WEAREDISCOVEREDFLEEATONCE" }, // decode 22 new[] { "WEAREDISCOVEREDFLEEATONCE", "WCLEESOFECAIVDENRDEEAOERT" }, // encode 23 new[] { "WECRLTEERDSOEEFEAOCAIVDEN", "WLSADOOTEEECEAEECRFINVEDR" } // decode 24 }; 25 int[] rails = { 3, 2, 4, 4, 3, 3, 3, 3, 4, 4, 5, 5 }; 26 for (int i = 0; i < config.Length; i++) 27 { 28 var actual = i % 2 == 0 || i == 1 29 ? RailFenceCipher.Encode(config[i][0], rails[i]) 30 : RailFenceCipher.Decode(config[i][0], rails[i]); 31 32 Assert.AreEqual(config[i][1], actual); 33 } 34 } 35 36 /* ***************** 37 * RANDOM TESTS 38 * *****************/ 39 40 private class Sol 41 { 42 private static IEnumerable<T> Fencer<T>(int n, IEnumerable<T> str) 43 { 44 var rails = Enumerable.Range(0, n).Select(r => new List<T>()).ToList(); 45 int[] data = { 0, 1 }; 46 int x = 0, dx = 1; 47 foreach (var t in str) 48 { 49 rails[data[x]].Add(t); 50 if (data[x] == n - 1 && data[dx] > 0 || data[x] == 0 && data[dx] < 0) 51 data[dx] *= -1; 52 data[x] += data[dx]; 53 } 54 return rails.SelectMany(lst => lst); 55 } 56 57 public static string Encode(string s, int n) => new string(Fencer(n, s).ToArray()); 58 59 public static string Decode(string s, int n) 60 { 61 char[] arr = new char[s.Length]; 62 int[] j = { 0 }; 63 Fencer(n, Enumerable.Range(0, s.Length)).ToList().ForEach(i => arr[i] = s[j[0]++]); 64 return new string(arr); 65 } 66 } 67 68 private Random rnd = new Random(); 69 70 private int Rand(int start, int end) { return start + rnd.Next(end - start); } 71 72 [Test] 73 public void FixedStringVariousRails() 74 { 75 var msg = "WEAREDISCOVEREDFLEEATONCE"; 76 Console.WriteLine($"Input for these tests:\n{msg}"); 77 78 for (int r = 0; r < 10; r++) 79 { 80 var rails = Rand(2, 11); 81 var exp = Sol.Encode(msg, rails); 82 Assert.AreEqual(exp, RailFenceCipher.Encode(msg, rails)); 83 84 rails = Rand(2, 11); 85 exp = Sol.Decode(msg, rails); 86 Assert.AreEqual(exp, RailFenceCipher.Decode(msg, rails)); 87 } 88 } 89 90 private static string lorem = "Amet non facere minima iure unde, provident, " 91 + "veritatis officiis asperiores ipsa eveniet sit! Deserunt " 92 + "autem excepturi quibusdam iure unde! Porro alias distinctio " 93 + "ipsam iure exercitationem molestiae. Voluptate fugiat quasi maiores!jk"; 94 private static List<string> lorarr = lorem.Split(' ').ToList(); 95 96 [Test] 97 public void RandomTests() 98 { 99 Console.WriteLine($"Base string for these tests (or a shuffled version):\n{lorem}"); 100 101 for (int r = 0; r < 50; r++) 102 { 103 var msg = Shuffle(); 104 int rails = Rand(2, 51); 105 var exp = Sol.Encode(msg, rails); 106 Assert.AreEqual(exp, RailFenceCipher.Encode(msg, rails)); 107 108 msg = Shuffle(); 109 rails = Rand(2, 51); 110 exp = Sol.Decode(msg, rails); 111 Assert.AreEqual(exp, RailFenceCipher.Decode(msg, rails)); 112 } 113 } 114 115 private string Shuffle() 116 { 117 Shuffle(lorarr); 118 return string.Join(" ", lorarr); 119 } 120 121 public static void Shuffle<T>(List<T> deck) 122 { 123 var rnd = new Random(); 124 for (int n = deck.Count - 1; n > 0; --n) 125 { 126 int k = rnd.Next(n + 1); 127 T temp = deck[n]; 128 deck[n] = deck[k]; 129 deck[k] = temp; 130 } 131 } 132 }