Skip to content

Commit 06103a2

Browse files
committed
Content refactored.
1 parent ff781f5 commit 06103a2

File tree

1 file changed

+124
-192
lines changed

1 file changed

+124
-192
lines changed
Lines changed: 124 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
---
2-
32
title: Google FooBar First Challenge
43
subtitle: Solving Caesar Cipher
54
date: '2020-07-19'
@@ -8,254 +7,187 @@ keywords: ['google', 'challenge', 'series', 'cipher', 'first', 'caesar']
87
slug: google-foobar-first-challenge-caesar-cipher
98
cover: './img/google-foobar-first-challenge-solved.png'
109
type: 'BlogPost'
11-
1210
---
1311

12+
In this post I will share my experience solving the first `Google FooBar Challenge`.
1413

15-
### Background
16-
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 where I completed my first challenge.
14+
I received a surprise code challenge invitation from Google on July 26, 2020 while doing some search on Google. I had no previous idea about it, frankly never heard of it. I accepted the invitation and I was redirected to [FooBar](https://foobar.withgoogle.com). After log in, a `CLI Interface` with the instructions about the challenge and how to request a challenge was provided. The challenge had to be solved within 2 days.
1715

18-
In this post I will share my experience solving the challenge.
19-
20-
21-
### The Problem: Caesar Cipher Decryption
16+
### The Challenge
2217
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".
2318

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-
19+
__Constraints__
20+
- Java Runtime Environment(JRE) 8.
21+
- Limited execution time.
22+
- Prohibit usage of third-party libraries.
23+
- Parallel procession not allowed.
24+
- Character limit to around 3000 (I don’t remember exactly).
3025

3126
### 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.
27+
The rough idea I could come up after the analysis was:
28+
- Manipulation of the ASCII value of the letters
29+
- Simple maths, addition/subtraction, on numeric value of letters
3330

31+
Since converting this idea to a concrete algorithm took time, I moved to another solution.
3432

3533
### 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-
34+
The next simplest design for the solution I could come up with consisted of the following data structures:
35+
- `HashMap` to store the entire `a` to `z` encryption for easy access of decrypted letters.
36+
- `StringBuilder` to store the deciphered text.
37+
- Use `Java 8` new method `String.chars()` API which returns a stream of `int` (IntStream) that represents the character codes.
3838
```java
3939
public static String solnWithStrBuilder(String x) {
40-
StringBuilder stb = new StringBuilder();
41-
x.chars().forEach(i -> stb.append(decrypt(i)));
42-
return stb.toString();
40+
StringBuilder stb = new StringBuilder();
41+
x.chars().forEach(i -> stb.append(decrypt(i)));
42+
return stb.toString();
4343
}
4444
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;
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;
5555
}
5656
```
57-
58-
57+
The implementation provided desired output in the local environment but it failed to pass the tests.
58+
5959
### 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-
60+
I reflected back on the constraints and refactored the code to use more primitive data structure:
61+
- Replaced `StringBuilder` with `Character Array`.
6462
```java
6563
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);
64+
char[] encryptedCharArr = new char[encryptedText.length()];
65+
Map<Integer, Character> decryptKey = new HashMap<>();
66+
char encodedChar;
67+
for (int i = 122, j = 97; i >= 97; i--, j++) {
68+
encodedChar = (char) i;
69+
decryptKey.put(j, encodedChar);
70+
}
71+
for (int i = 0; i < encryptedText.length(); i++) {
72+
char encryptedVal = encryptedText.charAt(i);
73+
if ( encryptedVal >= 97 && encryptedVal <= 122) {
74+
encryptedCharArr[i] = decryptKey.get((int)encryptedVal);
75+
} else encryptedCharArr[i] = encryptedVal;
76+
}
77+
return new String(encryptedCharArr);
8478
}
8579
```
86-
80+
But again the solution did not pass!
81+
At this point I got getting a little bit frustrated because there were no proper errors thrown. The output was just a list of failed tests.
82+
I lost interest after around an hour and I went back to my previous preparation method, solving challenges from other sources.
8783

8884
### 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.
85+
The next day I went back to the challenge with around 3 hours remaining. To make sure the time spent here would be worthwhile, I did some search to know more about the challenge. Only then I realized that the challenge was `invitation only`, and was used by Google for recruitment. Those were good enough reasons for me to continue, but then I was seriously running out of time.
9286

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.
87+
Then I searched for similar problems and solutions to find answers to these questions:
88+
- How to encrypt a letter by another?
89+
- What are the standard encryption algorithms?
9490

91+
A simple example by [Baeldung](https://www.baeldung.com/java-caesar-cipher) answered my queries.
9592
```java
9693
StringBuilder result = new StringBuilder();
9794
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-
}
95+
if (character != ' ') {
96+
int originalAlphabetPosition = character - 'a';
97+
int newAlphabetPosition = (originalAlphabetPosition + offset) % 26;
98+
char newCharacter = (char) ('a' + newAlphabetPosition);
99+
result.append(newCharacter);
100+
} else {
101+
result.append(character);
102+
}
106103
}
107-
104+
108105
return result;
109-
```
106+
```
107+
108+
`“Any fool can know. The point is to understand.” - Albert Einstein`
109+
110+
The standard encryption algorithm used was `Caesar Cipher`. Knowing that the encryption was `Caesar Cipher`, I wanted to understand more which led to [Cryptography](https://github.com/codeanit/til/issues/43), [Cipher](https://github.com/codeanit/til/issues/107) and [more](https://github.com/codeanit/til/issues). Keeping limited time to mind, I skimmed the contents.
110111

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+
`A **Cipher** is a method for encrypting a message, intending to make it less readable. **Caesar Cipher** is a substitution cipher that transforms a message by shifting its letters by a given offset.`
112113

114+
Baeldung's post also made me realize that understanding of the encryption process was important to decipher. Therefore, based on Baeldung's framework, I created an encryption system that generated the cipher from the challenge.
113115
```java
114116
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);
117+
StringBuilder result = new StringBuilder();
118+
for (char character : textToEncrypt.toCharArray()) {
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+
} else {
124+
result.append(character);
125+
}
126+
}
127+
return new String(result);
130128
}
131129
```
132130

133-
Then creating a decryption mechanism was some maths and common sense. Then again, the solution failed.
134-
131+
After generating the exact cipher, It was pretty much easier to write a program to decipher.
135132
```java
136133
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);
134+
StringBuilder result = new StringBuilder();
135+
for (char character : encryptedText.toCharArray()) {
136+
if ( (int)character >= 97 && (int)character <= 122 ) {
137+
int originalAlphabetPosition = character - 'z';
138+
int newAlphabetPosition = (originalAlphabetPosition + 25 ) % 26;
139+
result.append((char) ('z' + newAlphabetPosition));
140+
} else {
141+
result.append(character);
142+
}
143+
}
144+
return new String(result);
152145
}
153146
```
147+
I submitted the solution then again it failed.
154148

149+
__Time on the clock less than 10mins left!__
155150

156151
### 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-
152+
Reflected back on the constraints, and refactored the code
153+
- Replaced `StringBuilder` with `Character Array`.
159154
```java
160155
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-
}
156+
int strLength = x.length();
157+
char[] encryptedCharArr = new char[strLength];
158+
for (int i = 0; i < strLength; i++) {
159+
int character = x.charAt(i);
160+
if ( character >= 97 && character <= 122 ) {
161+
int originalAlphabetPosition = character - 'z';
162+
int newAlphabetPosition = (originalAlphabetPosition + 25 ) % 26;
163+
encryptedCharArr[i] = (char) ('z' + newAlphabetPosition);
164+
} else {
165+
encryptedCharArr[i] = (char)character;
166+
}
167+
}
168+
return new String(encryptedCharArr);
227169
}
228170
```
171+
__YAY!!! Solution Passed All Tests!__
229172

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-
}
173+
__Time on the clock less than 2mins__
174+
175+
### Review
176+
The next day I reviewed the solution and recalled the overall process. Phew! It was a close call.
245177

246-
return new String(decryptedCharArr);
247-
}
248-
```
178+
I wanted to refactor the submitted code as had I submitted in a hurry. Before refactoring, I wrote `Unit Tests` to make sure nothing is broken after the changes are made. These 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.
249179

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.
180+
Google's first code challenge was fun, I turned it to an exhilarating moment :joy:.
252181

253182
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.
254183

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`.
184+
I am grateful for both opportunities but I must say I have enjoyed this experience more may be because I'm more of a code person.
256185

257-
Thank you Google!
186+
__Thank you Google!__
258187

259188

260189
If code is a better language to you then please find more at [Github](https://github.com/JavaCheatsheet/codechallenge).
261190

191+
192+
193+

0 commit comments

Comments
 (0)