Try   HackMD

#2 Caesar Cipher Encryptor

tags: String Easy

Problem

Given a non-empty string of lowercase letters and a non-negative integer
representing a key, write a function that returns a new string obtained by
shifting every letter in the input string by k positions in the alphabet,
where k is the key.

Note that letters should "wrap" around the alphabet; in other words, the
letter z shifted by one returns the letter a.

Sample Input

string = "xyz"
key = 2

Sample Output

"zab"

Optimal Space & Time Complexity
O(n) time | O(n) space - where n is the length of the input string

Hint 1

Most languages have built-in functions that give you the Unicode value of a character as well as the character corresponding to a Unicode value. Consider using such functions to determine which letters the input string's letters should be mapped to.


Hint 2

Try creating your own mapping of letters to codes. In other words, try associating each letter in the alphabet with a specific number - its position in the alphabet, for instance - and using that to determine which letters the input string's letters should be mapped to.


Hint 3

How do you handle cases where a letter gets shifted to a position that requires wrapping around the alphabet? What about cases where the key is very large and causes multiple wrappings around the alphabet? The modulo operator should be your friend here.



Solutions

Official 1

function caesarCipherEncryptor(string, key) { const newLetters = []; const newKey = key % 26; for (const letter of string){ newLetters.push(getNewLetter(letter, newKey)); } return newLetters.join(''); } function getNewLetter(letter, key){ const newLetterCode = letter.charCodeAt() + key; return newLetterCode <= 122 ? String.fromCharCode(newLetterCode):String.fromCharCode(96+(newLetterCode % 122)); } // Do not edit the line below. exports.caesarCipherEncryptor = caesarCipherEncryptor;
詳解
string = "xyz" key = 2 function caesarCipherEncryptor(string, key) { const newLetters = []; const newKey = key % 26; //key對26取餘數 for (const letter of string){ newLetters.push(getNewLetter(letter, newKey)); /* newLetters.push('z'); newLetters.push('a'); newLetters.push('b'); */ } return newLetters.join(''); //['z','a','b'] -> 'zab' } function getNewLetter(letter, key){ const newLetterCode = letter.charCodeAt() + key; /* key = 2 'x'.charCodeAt() => 120, 120+2= 122 'y'.charCodeAt() => 121, 121+2= 123 'z'.charCodeAt() => 122, 122+2= 124 newLetterCode = 122, 123, 124 */ return newLetterCode <= 122 ? String.fromCharCode(newLetterCode):String.fromCharCode(96+(newLetterCode % 122)); /* 122 -> String.fromCharCode(122) -> 'z' 123 -> String.fromCharCode(96 + (123 % 122)) -> 'a' 124 -> String.fromCharCode(96 + (124 % 122)) -> 'b' return 'z', 'a', 'b' */ }

Official 2

function caesarCipherEncryptor(string, key) { const newLetters = []; const newKey = key % 26; const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); for (const letter of string){ newLetters.push(getNewLetter(letter, newKey, alphabet)); } return newLetters.join(''); } function getNewLetter(letter, key, alphabet){ const newLetterCode = alphabet.indexOf(letter)+key; return alphabet[newLetterCode % 26]; } // Do not edit the line below. exports.caesarCipherEncryptor = caesarCipherEncryptor;
詳解
string = "xyz" key = 2 function caesarCipherEncryptor(string, key) { const newLetters = []; const newKey = key % 26; //key對26取餘數 const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split(''); //["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] for (const letter of string){ newLetters.push(getNewLetter(letter, newKey, alphabet)); /* newLetters.push('z'); newLetters.push('a'); newLetters.push('b'); */ } return newLetters.join(''); //['z','a','b'] -> 'zab' } function getNewLetter(letter, key, alphabet){ const newLetterCode = alphabet.indexOf(letter)+key; /* alphabet.indexOf('x')=23 ,23+2= 25 alphabet.indexOf('y')=24 ,24+2= 26 alphabet.indexOf('z')=25 ,25+2= 27 newLetterCode = 25, 26, 27 */ return alphabet[newLetterCode % 26]; //alphabet[25],alphabet[0],alphabet[1] //'z', 'a', 'b' }


Everyone's

// Time O(n) Space O(n) function caesarCipherEncryptor(string, key){ const START_CODE = "a".charCodeAt(), END_CODE = "z".charCodeAt(); const A_ROUND = END_CODE - START_CODE + 1; const len = string.length; string = [...string]; for(let i = 0; i < len ; i++){ const setCode = ()=>{ let code = string[i].charCodeAt() + key; while(code > END_CODE){ code -= A_ROUND; } return code } string[i] = String.fromCodePoint(setCode()); } return string.join("") }

Raiy
//Time O(n) Space O(n) function caesarCipherEncryptor(string, key) { const originalTableArr = "abcdefghijklmnopqrstuvwxyz".split(""); const shiftKey = key % 26; // 2 let result = ""; for(let i = 0; i < string.length; i++){ const newLetterIdx = originalTableArr.indexOf(string[i])+shiftKey; const newLetter = originalTableArr[newLetterIdx%26] result += newLetter; } return result }

Becky
function caesarCipherEncryptor(string, key) { let len = string.length; let n; let ans = ''; key = key % 26; for(let i=0; i< len; i++){ n = string.charCodeAt(i); n = n+key; if(n > 122){ letter = String.fromCharCode(n-26); }else{ letter = String.fromCharCode(n); } ans = ans+letter; } return ans; } exports.caesarCipherEncryptor = caesarCipherEncryptor;

// O(n^2) time | O(n) space - n is the length or string function caesarCipherEncryptor(string, key) { const rangeStart = "a".charCodeAt(0); let res = ""; for (let i = 0; i < string.length; i++) { const charCode = (string.charCodeAt(i) + key - rangeStart) % 26 + rangeStart ; res += String.fromCharCode(charCode); } return res; }

Hao
function caesarCipherEncryptor(string, key) { /** * O(n) time, O(n) space */ const aCharCode = 'a'.charCodeAt(0); const charArr = string.split(''); const charTotal = 26; return charArr.reduce((acc, value) => acc + String.fromCharCode((value.charCodeAt('0') - aCharCode + key) % charTotal + aCharCode), ''); };

YC
/* time: O(n); - where n is the length of the input string space: O(n); - where n is the length of the input string */ function encryptor(string, key){ const alphabetNum = 26; const shift = key % 26 const idxZ = "z".charCodeAt(0); const ans = []; for (const letter of string){ let codeIdx = letter.charCodeAt(0) + shift; if(codeIdx > idxZ) codeIdx -= alphabetNum; ans.push(String.fromCharCode(codeIdx)); } return ans.join(""); }

SOL
const getCode = () => { let a = ""; for (let i = 9; ++i < 36; ) { a += i.toString(36); } return a.split(""); }; function caesarCipherEncryptor(string, key) { const code = getCode(); return string .split("") .map((letter) => { const index = code.findIndex((item) => item === letter); let targtIndex = index + key; while (targtIndex >= code.length) { targtIndex = targtIndex - code.length; } return code[targtIndex]; }) .join(""); } exports.caesarCipherEncryptor = caesarCipherEncryptor;

Supplement / Discussion

凱撒密碼 (Caesar cipher)

是一種最簡單且最廣為人知的加密技術。凱撒密碼是一種替換加密技術,明文中的所有字母都在字母表上向後(或向前)按照一個固定數目進行偏移後被替換成密文。例如,當偏移量是3的時候,所有的字母A將被替換成D,B變成E,以此類推。
這個加密方法是以羅馬共和時期凱撒的名字命名的,據稱當年凱撒曾用此方法與其將軍們進行聯繫。
凱撒密碼通常被作為其他更複雜的加密方法中的一個步驟,但是和所有的利用字母表進行替換的加密技術一樣,凱撒密碼非常容易被破解,而且在實際應用中也無法保證通信安全。


charCodeAt()

String.prototype.charCodeAt()
返回一個介於 0 和 65535 之間的整數,表示給定索引處的 UTF-16 代碼單元。
用法:

string = "xyz" console.log(string.charCodeAt(2)); //122 //a-z 是 97-122

補充:UTF-16
Unicode 是國際標準字符集,它將世界各種語言的每個字符定義一個唯一的編碼,以滿足跨語言、跨平臺的文本信息轉換。
UTF-16 是一種變長字符編碼, 這種編碼方式比較特殊, 它將字符編碼成 2 字節 或者 4 字節。

英文字母 a-z 是 97-122
數字 0-9 是 48-57

charCodeAt()的參數:

  1. index
  2. 如果index無法轉換為整數或未提供,則以0作為默認值。
string = "xyz" console.log(string.charCodeAt()); //120
  1. 如果index為負數或超出範圍,則 charCodeAt() 返回 NaN。
string = "xyz" console.log(string.charCodeAt(-5)); //NaN

String.fromCharCode()

String.fromCharCode()
返回由指定的 UTF-16 代碼單元序列創建的字串。

console.log(String.fromCharCode(122)); //'z'

fromCharCode()的參數:

  1. UTF-16的代碼(範圍介於 0 到 65535(0xFFFF)之間
  2. 大於 0xFFFF 的數字會直接回傳空字串。
console.log(String.fromCharCode(65536)); //''
  1. 可以一次傳入多個參數
console.log(String.fromCharCode(120, 121, 122)); //'xyz'