Skip to content

Commit c920587

Browse files
authored
Merge pull request #31 from codeanitdotcom/content#30-Caesar_Cipher
Publish post `Google FooBar First Challenge`.
2 parents 6a19747 + 34605a4 commit c920587

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
---
2+
3+
title: Google FooBar First Challenge
4+
subtitle: Solving Caesar Cipher
5+
date: '2020-07-19'
6+
categories: ['google', 'challenge', 'series', 'cipher']
7+
keywords: ['google', 'challenge', 'series', 'cipher', 'first', 'caesar']
8+
slug: google-foobar-first-challenge-caesar-cipher
9+
cover: './img/google-foobar-first-challenge-solved.png'
10+
type: 'BlogPost'
11+
12+
---
13+
14+
15+
### Background
16+
In my quest of searching for the next opportunity and in the process of preparing technical interviews, I received a surprise code challenge invitation from Google on July 26, 2020. After accepting the invitation, I was redirected to https://foobar.withgoogle.com.
17+
18+
I am going to record my experience with the code challenge experience.
19+
20+
21+
### The Problem: Caesar Cipher Decryption
22+
Decrypt a code where every lowercase letter [a..z] is replaced with the corresponding one in [z..a], while every other character (including uppercase letters and punctuation) is left untouched. That is, 'a' becomes 'z', 'b' becomes 'y', 'c' becomes 'x', etc. For instance, the word "vmxibkgrlm", when decoded, would become "encryption".
23+
24+
There was some limitation to execution time and the external Java library usage environment provided was default Java Runtime Environment (JRE) 8.
25+
26+
27+
### What is Caesar Cipher?
28+
A `Cipher` is a method for encrypting a message, intending to make it less readable. As for the `Caesar Cipher`, it's a substitution cipher that transforms a message by shifting its letters by a given offset.
29+
30+
31+
### Initial Thoughts
32+
After going through the problem and some analysis, the rough idea I could come up with as a part of the solution was manipulating the ASCII value of the letters. Addition and subtraction of numeric value of numbers should do the trick but at this point I did not think of `Caesar Cipher` at all. Implementing the initial idea to anything concrete was taking some time so I went with the simplest solution that I could work out.
33+
34+
35+
### First Attempt
36+
To solve the problem I needed a data structure to keep a map of the encryption and easily access the decrypted value. `HashMap` was the best fit. The other part of the solution required a data structure to store the decrypted characters as a `String`. Although `Character Array` would have been the ideal way, `StringBuilder` with `Java 8` `SteamAPI` was the quickest option that did the trick. Hence, I went with the implementation built based on these base data types. The rough solution did the job and ran successfully at my local but sadly it did not pass the challenge tests.
37+
38+
```java
39+
public static String solnWithStrBuilder(String x) {
40+
StringBuilder stb = new StringBuilder();
41+
x.chars().forEach(i -> stb.append(decrypt(i)));
42+
return stb.toString();
43+
}
44+
public static char decrypt(int encryptedVal) {
45+
if ( encryptedVal >= 97 && encryptedVal <= 122) {
46+
char encodedChar;
47+
Map<Integer, Character> decryptKey = new HashMap<>();
48+
for (int i = 122, j = 97; i >= 97; i--, j++) {
49+
encodedChar = (char) i;
50+
decryptKey.put(j, encodedChar);
51+
}
52+
return decryptKey.get(encryptedVal);
53+
}
54+
else return (char)encryptedVal;
55+
}
56+
```
57+
58+
59+
### Second Iteration
60+
I reflected back on the limitations. I knew a cheaper data structure, `Character Array`, could have done the job instead of `StringBuilder` class. I thought that might be the reason that the solution failed to fulfill the criteria, so I refactored the code replacing StringBuilder with a simple Character Array. But this solution also failed to pass any of the tests.
61+
62+
At this point I was getting a little bit frustrated because the errors were not thrown back properly. I was not sure what failed. To be honest, I had lost interest after one hour of trials and gave up. I needed to prepare for interviews and I went back to my previous way, learning basics and solving challenges from different sources.
63+
64+
```java
65+
public static String solnWithArray(String encryptedText) {
66+
char[] encryptedCharArr = new char[encryptedText.length()];
67+
Map<Integer, Character> decryptKey = new HashMap<>();
68+
char encodedChar;
69+
70+
for (int i = 122, j = 97; i >= 97; i--, j++) {
71+
encodedChar = (char) i;
72+
decryptKey.put(j, encodedChar);
73+
}
74+
75+
for (int i = 0; i < encryptedText.length(); i++) {
76+
char encryptedVal = encryptedText.charAt(i);
77+
if ( encryptedVal >= 97 && encryptedVal <= 122) {
78+
encryptedCharArr[i] = decryptKey.get((int)encryptedVal);
79+
80+
} else encryptedCharArr[i] = encryptedVal;
81+
}
82+
83+
return new String(encryptedCharArr);
84+
}
85+
```
86+
87+
88+
### Third Iteration
89+
I wanted to solve the problem and came back to it again with less than 3 hours left to complete the other day.
90+
91+
As I had never participated in `Google FooBar Challenge`, and even never heard of it, I wanted to know more as I did not want to waste more time if there would be no value in participating in the event, and I needed to continue my preparation. So after some search, I knew more about the challenge and that it was an invitation only and used by Google for recruitment. It added more motivation to solve the problem but then I was running out of time.
92+
93+
I reflected back on the problem statement and the constraints again. As I knew that there would be other ASCII based optimum solutions which might satisfy the conditions, I did some more research on ways to encrypt letters with other letters and other standard encryption processes. It led to the `Cesar Cipher` which was the term that I was looking for. After digging more into it, I found a simple example by [Baeldung.com](https://www.baeldung.com/java-caesar-cipher) which laid the foundation to my initial ASCII based solution.
94+
95+
```java
96+
StringBuilder result = new StringBuilder();
97+
for (char character : message.toCharArray()) {
98+
if (character != ' ') {
99+
int originalAlphabetPosition = character - 'a';
100+
int newAlphabetPosition = (originalAlphabetPosition + offset) % 26;
101+
char newCharacter = (char) ('a' + newAlphabetPosition);
102+
result.append(newCharacter);
103+
} else {
104+
result.append(character);
105+
}
106+
}
107+
108+
return result;
109+
```
110+
111+
Baeldung's example `encrypts` letters. It did not `decrypt`. Letter `a` was used as the base for the encryption process. Understanding the encryption process was very important to successfully decrypt the cipher. So based on this framework I created an encryption system that generates the cipher which is our problem.
112+
113+
```java
114+
public static String encrypt(String textToEncrypt) {
115+
StringBuilder result = new StringBuilder();
116+
117+
for (char character : textToEncrypt.toCharArray()) {
118+
119+
if ( (int)character >= 97 && (int)character <= 122 ) {
120+
int originalAlphabetPosition = character - 'z';
121+
int newAlphabetPosition = ( originalAlphabetPosition + 25 ) % 26;
122+
result.append((char) ('z' - newAlphabetPosition));
123+
124+
} else {
125+
result.append(character);
126+
}
127+
}
128+
129+
return new String(result);
130+
}
131+
```
132+
133+
Then creating a decryption mechanism was some maths and common sense. Then again, the solution failed.
134+
135+
```java
136+
public static String solutionWithOffset(String encryptedText) {
137+
StringBuilder result = new StringBuilder();
138+
139+
for (char character : encryptedText.toCharArray()) {
140+
141+
if ( (int)character >= 97 && (int)character <= 122 ) {
142+
int originalAlphabetPosition = character - 'z';
143+
int newAlphabetPosition = (originalAlphabetPosition + 25 ) % 26;
144+
result.append((char) ('z' + newAlphabetPosition));
145+
146+
} else {
147+
result.append(character);
148+
}
149+
}
150+
151+
return new String(result);
152+
}
153+
```
154+
155+
156+
### Accepted Solution
157+
The default example had implemented StringBuilder class which I thought was the reason for the failed tests. I replaced StringBuilder with Array implementation and the solution passed all the tests. Yay!
158+
159+
```java
160+
public static String solution(String x) {
161+
int strLength = x.length();
162+
char[] encryptedCharArr = new char[strLength];
163+
164+
for (int i = 0; i < strLength; i++) {
165+
int character = x.charAt(i);
166+
167+
if ( character >= 97 && character <= 122 ) {
168+
int originalAlphabetPosition = character - 'z';
169+
int newAlphabetPosition = (originalAlphabetPosition + 25 ) % 26;
170+
encryptedCharArr[i] = (char) ('z' + newAlphabetPosition);
171+
172+
} else {
173+
encryptedCharArr[i] = (char)character;
174+
}
175+
}
176+
177+
return new String(encryptedCharArr);
178+
}
179+
```
180+
181+
182+
### Further Refactoring, Test Harness
183+
After submitting the solution I reviewed the solution and the recalled overall process. Being a firm believer of `Test Driven Development (TDD)`, I wanted safety to keep the behaviours of the solution the same, I wrote simple `Unit Tests`. The unit tests did not need to be extensive covering all edge cases; neither should have many use cases covered as it had already passed the test cases of Google. I just needed something to preserve what was functional in my local.
184+
185+
```java
186+
class CaesarCipherTest {
187+
188+
String result = "wrw blf hvv ozhg mrtsg'h vkrhlwv?";
189+
String expected = "did you see last night's episode?";
190+
191+
@Test
192+
void solution() {
193+
String actual = CaesarCipher.solution(result);
194+
Assertions.assertEquals(expected, actual);
195+
}
196+
197+
@Test
198+
void submittedSolution() {
199+
String actual = CaesarCipher.submittedSolution(result);
200+
Assertions.assertEquals(expected, actual);
201+
}
202+
203+
@Test
204+
void encrypt() {
205+
String expected = "wrw blf hvv ozhg mrtsg'h vkrhlwv?";
206+
String plainText = "did you see last night's episode?";
207+
result = CaesarCipher.encrypt(plainText);
208+
Assertions.assertEquals(expected, result);
209+
}
210+
211+
@Test
212+
void solnWithArray() {
213+
Assertions.assertEquals( expected, CaesarCipher.solnWithArray(result) );
214+
}
215+
216+
@Test
217+
void solnWithStrBuilder() {
218+
Assertions.assertEquals(expected, CaesarCipher.solnWithArray(result) );
219+
}
220+
221+
@Test
222+
void decrypt() {
223+
char expected = 'z';
224+
char result= 'a';
225+
Assertions.assertEquals( expected , CaesarCipher.decrypt(result) );
226+
}
227+
}
228+
```
229+
230+
Now with the protection of the tests, I refactored the submitted solution.
231+
232+
```java
233+
public static String solution(String cipher) {
234+
int cipherLen = cipher.length();
235+
char[] decryptedCharArr = new char[cipherLen];
236+
237+
for (int i = 0; i < cipherLen; i++) {
238+
int character = cipher.charAt(i);
239+
240+
if ( character >= 97 && character <= 122 )
241+
decryptedCharArr[i] = (char) ( 'z' - ( ( character + 7) % 26 ));
242+
else
243+
decryptedCharArr[i] = (char)character;
244+
}
245+
246+
return new String(decryptedCharArr);
247+
}
248+
```
249+
250+
### Review
251+
`Google FooBar Code Challenge` was fun. Sometimes `the unknown` leads to better results as if there had been clear test case failure outputs, It would not have pushed myself to think and rethink more. On the other hand, I would have found the solution straight forward but problems in real life are also abstract. The constraints and the test cases pushed me to use optimum data structures and algorithms.
252+
253+
Previously, I had an interview with Google in July 2019. It was more of a casual talk I had with a nice lady who wanted to know a bit of me and to see if I fit the available role at a certain location. Unfortunately, it did not work out.
254+
255+
I am grateful to both opportunities but I am enjoying this experience more that may be because I'm more of a `code person`.
256+
257+
Thank you Google!
258+
259+
260+
If you code is a better language to you then please find more at [Github](https://github.com/JavaCheatsheet/codechallenge).
261+
Loading

0 commit comments

Comments
 (0)