Skip to content

Instantly share code, notes, and snippets.

@ashikuzzaman-ar
Last active September 2, 2021 08:26
Show Gist options
  • Save ashikuzzaman-ar/bea79663bb7a1eea47814a29b1a84241 to your computer and use it in GitHub Desktop.
Save ashikuzzaman-ar/bea79663bb7a1eea47814a29b1a84241 to your computer and use it in GitHub Desktop.
Encrypt text in array diagonal and decrypt diagonally encrypted string to the original text.
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;
public class DiagonalEncoderDecoder {
/**
* String = "my name is"
* Word count = 3
* Replace spaces by '_' = "my_name_is"
* Put that to a 2D array where row size is 3 (word count)
* ___________
* |m|n|e|s| |
* ___________
* | |y|a|_| |
* ___________
* | | |_|m|i|
* ___________
* <p>
* Now replace all empty spaces by '_'
* ___________
* |m|n|e|s|_|
* ___________
* |_|y|a|_|_|
* ___________
* |_|_|_|m|i|
* ___________
* <p>
* Now merge all rows in a single string
* <p>
* Encoded String = "mnes__ya_____mi"
*
* @param text String
* @return String
*/
public static String encoder(String text) {
if (text == null) {
return null;
}
//Number of word(s) separated by space.
int wordCount = wordCount(text);
//List of lists for character matrix.
//Character matrix will dynamically grow that is why using list instead of array.
//In case of 2D array, array size will be (n x m) where n = wordCount and m = (text.length() / n + (n - 1))
List<List<Character>> characters = new ArrayList<>(wordCount);
//Initializing lists and inserting incremental '_' for each row so that next insertion occurs diagonally
for (int i = 0; i < wordCount; i++) {
List<Character> list = new ArrayList<>();
for (int j = 0; j < i; j++) {
list.add('_');
}
characters.add(list);
}
int counter = 0;
int textLength = text.length();
//Inserting each character of the source diagonally in matrix
while (counter < textLength) {
for (int i = 0; i < wordCount; i++) {
if (counter < textLength) {
char c = text.charAt(counter++);
//Inserting character and replacing space by '_'
characters.get(i).add(c == ' ' ? '_' : c);
}
}
}
//Eventually the last row will be the longest
int maxSize = characters.get(wordCount - 1).size();
//Filling top rows by '_' up to the size of maxSize
for (List<Character> character : characters) {
while (character.size() < maxSize) {
character.add('_');
}
}
//Merging all characters to a single line string and returning the result
return characters.stream().parallel().flatMap(Collection::stream).map(String::valueOf).collect(Collectors.joining(""));
}
/**
* Count number of words in a string separated by space and returning the number
*
* @param text String
* @return int
*/
public static int wordCount(String text) {
return text == null ? 0 : text.split("\\s").length;
}
/**
* Given an encoded string and number of encoded worlds in the string.
* The string is encoded diagonally such that,
* encodedString = "mnes__ya_____mi"
* wordCount = 3
* ___________
* |m|n|e|s|_|
* ___________
* |_|y|a|_|_|
* ___________
* |_|_|_|m|i|
* ___________
* <p>
* The following 2D array with row number equals to word count has indexed (0, 0) = 'm', (1, 1) = 'y',
* (2, 2) = '_'. You have to return to the top again from (0, 1) = 'n', (1, 2) = 'a' and so on. You will
* find a string of three words which is "my_name_is", separated by '_'. Now replace '_' by space, and
* you will find decoded string which is "my name is". Empty spaces from both side will be trimmed.
*
* @param wordCount int
* @param encodedString String
* @return String
*/
public static String decoder(int wordCount, String encodedString) {
if (encodedString == null || wordCount == 0) {
return null;
}
int length = encodedString.length();
int rowLength = length / wordCount;
//Row length cannot be less than word count
if (rowLength < wordCount) {
throw new RuntimeException("Invalid world count " + wordCount);
}
//part list is the [wordCount] rows of the matrix
List<String> part = new ArrayList<>(wordCount);
for (int i = 0; i < wordCount; i++) {
int beginIndex = i * rowLength;
int endIndex = beginIndex + rowLength;
/*
* ___________
* |m|n|e|s|_|
* ___________
* |_|y|a|_|_|
* ___________
* |_|_|_|m|i|
* ___________
* Here we are ignoring all unnecessary '_' from the rows so that we can travers horizontally later.
* Final list looks like,
* ___________
* |m|n|e|s|_|
* ___________
* |y|a|_|_|
* _________
* |_|m|i|
* _______
*/
part.add(encodedString.substring(beginIndex + i, endIndex));
}
StringBuilder builder = new StringBuilder();
//We are traversing from beginning to the length of row length (length of initial row) for each row.
for (int i = 0; i < rowLength; i++) {
for (int j = 0; j < wordCount; j++) {
//Each row length is not same as first row, we are checking whether row length is accessible or not.
if (part.get(j).length() > i) {
builder.append(part.get(j).charAt(i));
}
}
}
//Finally, replacing '_' by space and returning after trimming.
return builder.toString().replace("_", " ").trim();
}
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
String text = scanner.nextLine().trim();
System.out.println("Original => " + text);
String encodedString = encoder(text);
System.out.println("Encoded => " + encodedString);
String decodedString = decoder(wordCount(text), encodedString);
System.out.println("Decoded => " + decodedString);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment