diff --git a/README.md b/README.md index d60d5104c385..6961106eb411 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,7 @@ Please read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute ## Algorithms Our [directory](DIRECTORY.md) has the full list of applications. + +### Contribution +- Added by Danielle Ihm for CIS175 GitHub fork assignment +- Practiced cloning, editing, committing, and pull requests diff --git a/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java b/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java index 624a4e2b858a..7cf1e43dabc1 100644 --- a/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java +++ b/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java @@ -1,68 +1,68 @@ -package com.thealgorithms.bitmanipulation; - -/** - * A utility class for performing single-bit operations on integers. - * These operations include flipping, setting, clearing, and getting - * individual bits at specified positions. - * - * Bit positions are zero-indexed (i.e., the least significant bit is at position 0). - * These methods leverage bitwise operations for optimal performance. - * - * Examples: - * - `flipBit(3, 1)` flips the bit at index 1 in binary `11` (result: `1`). - * - `setBit(4, 0)` sets the bit at index 0 in `100` (result: `101` or 5). - * - `clearBit(7, 1)` clears the bit at index 1 in `111` (result: `101` or 5). - * - `getBit(6, 0)` checks if the least significant bit is set (result: `0`). - * - * Time Complexity: O(1) for all operations. - * - * Author: lukasb1b (https://github.com/lukasb1b) - */ -public final class SingleBitOperations { - private SingleBitOperations() { - } - - /** - * Flips (toggles) the bit at the specified position. - * - * @param num the input number - * @param bit the position of the bit to flip (0-indexed) - * @return the new number after flipping the specified bit - */ - public static int flipBit(final int num, final int bit) { - return num ^ (1 << bit); - } - - /** - * Sets the bit at the specified position to 1. - * - * @param num the input number - * @param bit the position of the bit to set (0-indexed) - * @return the new number after setting the specified bit to 1 - */ - public static int setBit(final int num, final int bit) { - return num | (1 << bit); - } - - /** - * Clears the bit at the specified position (sets it to 0). - * - * @param num the input number - * @param bit the position of the bit to clear (0-indexed) - * @return the new number after clearing the specified bit - */ - public static int clearBit(final int num, final int bit) { - return num & ~(1 << bit); - } - - /** - * Gets the bit value (0 or 1) at the specified position. - * - * @param num the input number - * @param bit the position of the bit to retrieve (0-indexed) - * @return 1 if the bit is set, 0 otherwise - */ - public static int getBit(final int num, final int bit) { - return (num >> bit) & 1; - } -} +package com.thealgorithms.bitmanipulation; + +/** + * A utility class for performing single-bit operations on integers. + * These operations include flipping, setting, clearing, and getting + * individual bits at specified positions. + * + * Bit positions are zero-indexed (i.e., the least significant bit is at position 0). + * These methods leverage bitwise operations for optimal performance. + * + * Examples: + * - `flipBit(3, 1)` flips the bit at index 1 in binary `11` (result: `1`). + * - `setBit(4, 0)` sets the bit at index 0 in `100` (result: `101` or 5). + * - `clearBit(7, 1)` clears the bit at index 1 in `111` (result: `101` or 5). + * - `getBit(6, 0)` checks if the least significant bit is set (result: `0`). + * + * Time Complexity: O(1) for all operations. + * + * Author: lukasb1b (https://github.com/lukasb1b) + */ +public final class SingleBitOperations { + private SingleBitOperations() { + } + + /** + * Flips (toggles) the bit at the specified position. + * + * @param num the input number + * @param bit the position of the bit to flip (0-indexed) + * @return the new number after flipping the specified bit + */ + public static int flipBit(final int num, final int bit) { + return num ^ (1 << bit); + } + + /** + * Sets the bit at the specified position to 1. + * + * @param num the input number + * @param bit the position of the bit to set (0-indexed) + * @return the new number after setting the specified bit to 1 + */ + public static int setBit(final int num, final int bit) { + return num | (1 << bit); + } + + /** + * Clears the bit at the specified position (sets it to 0). + * + * @param num the input number + * @param bit the position of the bit to clear (0-indexed) + * @return the new number after clearing the specified bit + */ + public static int clearBit(final int num, final int bit) { + return num & ~(1 << bit); + } + + /** + * Gets the bit value (0 or 1) at the specified position. + * + * @param num the input number + * @param bit the position of the bit to retrieve (0-indexed) + * @return 1 if the bit is set, 0 otherwise + */ + public static int getBit(final int num, final int bit) { + return (num >> bit) & 1; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java b/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java index 9169aa82bd75..7e7baaa6f7d5 100644 --- a/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java @@ -1,101 +1,101 @@ -package com.thealgorithms.ciphers; - -/** - * The Atbash cipher is a classic substitution cipher that substitutes each letter - * with its opposite letter in the alphabet. - * - * For example: - * - 'A' becomes 'Z', 'B' becomes 'Y', 'C' becomes 'X', and so on. - * - Similarly, 'a' becomes 'z', 'b' becomes 'y', and so on. - * - * The cipher works identically for both uppercase and lowercase letters. - * Non-alphabetical characters remain unchanged in the output. - * - * This cipher is symmetric, meaning that applying the cipher twice will return - * the original text. Therefore, the same function is used for both encryption and decryption. - * - *

Usage Example:

- *
- * AtbashCipher cipher = new AtbashCipher("Hello World!");
- * String encrypted = cipher.convert(); // Output: "Svool Dliow!"
- * 
- * - * @author Krounosity - * @see Atbash Cipher (Wikipedia) - */ -public class AtbashCipher { - - private String toConvert; - - public AtbashCipher() { - } - - /** - * Constructor with a string parameter. - * - * @param str The string to be converted using the Atbash cipher - */ - public AtbashCipher(String str) { - this.toConvert = str; - } - - /** - * Returns the current string set for conversion. - * - * @return The string to be converted - */ - public String getString() { - return toConvert; - } - - /** - * Sets the string to be converted using the Atbash cipher. - * - * @param str The new string to convert - */ - public void setString(String str) { - this.toConvert = str; - } - - /** - * Checks if a character is uppercase. - * - * @param ch The character to check - * @return {@code true} if the character is uppercase, {@code false} otherwise - */ - private boolean isCapital(char ch) { - return ch >= 'A' && ch <= 'Z'; - } - - /** - * Checks if a character is lowercase. - * - * @param ch The character to check - * @return {@code true} if the character is lowercase, {@code false} otherwise - */ - private boolean isSmall(char ch) { - return ch >= 'a' && ch <= 'z'; - } - - /** - * Converts the input string using the Atbash cipher. - * Alphabetic characters are substituted with their opposite in the alphabet, - * while non-alphabetic characters remain unchanged. - * - * @return The converted string after applying the Atbash cipher - */ - public String convert() { - StringBuilder convertedString = new StringBuilder(); - - for (char ch : toConvert.toCharArray()) { - if (isSmall(ch)) { - convertedString.append((char) ('z' - (ch - 'a'))); - } else if (isCapital(ch)) { - convertedString.append((char) ('Z' - (ch - 'A'))); - } else { - convertedString.append(ch); - } - } - return convertedString.toString(); - } -} +package com.thealgorithms.ciphers; + +/** + * The Atbash cipher is a classic substitution cipher that substitutes each letter + * with its opposite letter in the alphabet. + * + * For example: + * - 'A' becomes 'Z', 'B' becomes 'Y', 'C' becomes 'X', and so on. + * - Similarly, 'a' becomes 'z', 'b' becomes 'y', and so on. + * + * The cipher works identically for both uppercase and lowercase letters. + * Non-alphabetical characters remain unchanged in the output. + * + * This cipher is symmetric, meaning that applying the cipher twice will return + * the original text. Therefore, the same function is used for both encryption and decryption. + * + *

Usage Example:

+ *
+ * AtbashCipher cipher = new AtbashCipher("Hello World!");
+ * String encrypted = cipher.convert(); // Output: "Svool Dliow!"
+ * 
+ * + * @author Krounosity + * @see Atbash Cipher (Wikipedia) + */ +public class AtbashCipher { + + private String toConvert; + + public AtbashCipher() { + } + + /** + * Constructor with a string parameter. + * + * @param str The string to be converted using the Atbash cipher + */ + public AtbashCipher(String str) { + this.toConvert = str; + } + + /** + * Returns the current string set for conversion. + * + * @return The string to be converted + */ + public String getString() { + return toConvert; + } + + /** + * Sets the string to be converted using the Atbash cipher. + * + * @param str The new string to convert + */ + public void setString(String str) { + this.toConvert = str; + } + + /** + * Checks if a character is uppercase. + * + * @param ch The character to check + * @return {@code true} if the character is uppercase, {@code false} otherwise + */ + private boolean isCapital(char ch) { + return ch >= 'A' && ch <= 'Z'; + } + + /** + * Checks if a character is lowercase. + * + * @param ch The character to check + * @return {@code true} if the character is lowercase, {@code false} otherwise + */ + private boolean isSmall(char ch) { + return ch >= 'a' && ch <= 'z'; + } + + /** + * Converts the input string using the Atbash cipher. + * Alphabetic characters are substituted with their opposite in the alphabet, + * while non-alphabetic characters remain unchanged. + * + * @return The converted string after applying the Atbash cipher + */ + public String convert() { + StringBuilder convertedString = new StringBuilder(); + + for (char ch : toConvert.toCharArray()) { + if (isSmall(ch)) { + convertedString.append((char) ('z' - (ch - 'a'))); + } else if (isCapital(ch)) { + convertedString.append((char) ('Z' - (ch - 'A'))); + } else { + convertedString.append(ch); + } + } + return convertedString.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/PermutationCipher.java b/src/main/java/com/thealgorithms/ciphers/PermutationCipher.java index ce443545db1d..26497f0f9955 100644 --- a/src/main/java/com/thealgorithms/ciphers/PermutationCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/PermutationCipher.java @@ -1,194 +1,194 @@ -package com.thealgorithms.ciphers; - -import java.util.HashSet; -import java.util.Set; - -/** - * A Java implementation of Permutation Cipher. - * It is a type of transposition cipher in which the plaintext is divided into blocks - * and the characters within each block are rearranged according to a fixed permutation key. - * - * For example, with key {3, 1, 2} and plaintext "HELLO", the text is divided into blocks - * of 3 characters: "HEL" and "LO" (with padding). The characters are then rearranged - * according to the key positions. - * - * @author GitHub Copilot - */ -public class PermutationCipher { - - private static final char PADDING_CHAR = 'X'; - - /** - * Encrypts the given plaintext using the permutation cipher with the specified key. - * - * @param plaintext the text to encrypt - * @param key the permutation key (array of integers representing positions) - * @return the encrypted text - * @throws IllegalArgumentException if the key is invalid - */ - public String encrypt(String plaintext, int[] key) { - validateKey(key); - - if (plaintext == null || plaintext.isEmpty()) { - return plaintext; - } - - // Remove spaces and convert to uppercase for consistent processing - String cleanText = plaintext.replaceAll("\\s+", "").toUpperCase(); - - // Pad the text to make it divisible by key length - String paddedText = padText(cleanText, key.length); - - StringBuilder encrypted = new StringBuilder(); - - // Process text in blocks of key length - for (int i = 0; i < paddedText.length(); i += key.length) { - String block = paddedText.substring(i, Math.min(i + key.length, paddedText.length())); - encrypted.append(permuteBlock(block, key)); - } - - return encrypted.toString(); - } - - /** - * Decrypts the given ciphertext using the permutation cipher with the specified key. - * - * @param ciphertext the text to decrypt - * @param key the permutation key (array of integers representing positions) - * @return the decrypted text - * @throws IllegalArgumentException if the key is invalid - */ - public String decrypt(String ciphertext, int[] key) { - validateKey(key); - - if (ciphertext == null || ciphertext.isEmpty()) { - return ciphertext; - } - - // Create the inverse permutation - int[] inverseKey = createInverseKey(key); - - StringBuilder decrypted = new StringBuilder(); - - // Process text in blocks of key length - for (int i = 0; i < ciphertext.length(); i += key.length) { - String block = ciphertext.substring(i, Math.min(i + key.length, ciphertext.length())); - decrypted.append(permuteBlock(block, inverseKey)); - } - - // Remove padding characters from the end - return removePadding(decrypted.toString()); - } - /** - * Validates that the permutation key is valid. - * A valid key must contain all integers from 1 to n exactly once, where n is the key length. - * - * @param key the permutation key to validate - * @throws IllegalArgumentException if the key is invalid - */ - private void validateKey(int[] key) { - if (key == null || key.length == 0) { - throw new IllegalArgumentException("Key cannot be null or empty"); - } - - Set keySet = new HashSet<>(); - for (int position : key) { - if (position < 1 || position > key.length) { - throw new IllegalArgumentException("Key must contain integers from 1 to " + key.length); - } - if (!keySet.add(position)) { - throw new IllegalArgumentException("Key must contain each position exactly once"); - } - } - } - - /** - * Pads the text with padding characters to make its length divisible by the block size. - * - * @param text the text to pad - * @param blockSize the size of each block - * @return the padded text - */ - private String padText(String text, int blockSize) { - int remainder = text.length() % blockSize; - if (remainder == 0) { - return text; - } - - int paddingNeeded = blockSize - remainder; - StringBuilder padded = new StringBuilder(text); - for (int i = 0; i < paddingNeeded; i++) { - padded.append(PADDING_CHAR); - } - - return padded.toString(); - } - /** - * Applies the permutation to a single block of text. - * - * @param block the block to permute - * @param key the permutation key - * @return the permuted block - */ - private String permuteBlock(String block, int[] key) { - if (block.length() != key.length) { - // Handle case where block is shorter than key (shouldn't happen with proper padding) - block = padText(block, key.length); - } - - char[] result = new char[key.length]; - char[] blockChars = block.toCharArray(); - - for (int i = 0; i < key.length; i++) { - // Key positions are 1-based, so subtract 1 for 0-based array indexing - result[i] = blockChars[key[i] - 1]; - } - - return new String(result); - } - - /** - * Creates the inverse permutation key for decryption. - * - * @param key the original permutation key - * @return the inverse key - */ - private int[] createInverseKey(int[] key) { - int[] inverse = new int[key.length]; - - for (int i = 0; i < key.length; i++) { - // The inverse key maps each position to where it should go - inverse[key[i] - 1] = i + 1; - } - - return inverse; - } - - /** - * Removes padding characters from the end of the decrypted text. - * - * @param text the text to remove padding from - * @return the text without padding - */ - private String removePadding(String text) { - if (text.isEmpty()) { - return text; - } - - int i = text.length() - 1; - while (i >= 0 && text.charAt(i) == PADDING_CHAR) { - i--; - } - - return text.substring(0, i + 1); - } - - /** - * Gets the padding character used by this cipher. - * - * @return the padding character - */ - public char getPaddingChar() { - return PADDING_CHAR; - } -} +package com.thealgorithms.ciphers; + +import java.util.HashSet; +import java.util.Set; + +/** + * A Java implementation of Permutation Cipher. + * It is a type of transposition cipher in which the plaintext is divided into blocks + * and the characters within each block are rearranged according to a fixed permutation key. + * + * For example, with key {3, 1, 2} and plaintext "HELLO", the text is divided into blocks + * of 3 characters: "HEL" and "LO" (with padding). The characters are then rearranged + * according to the key positions. + * + * @author GitHub Copilot + */ +public class PermutationCipher { + + private static final char PADDING_CHAR = 'X'; + + /** + * Encrypts the given plaintext using the permutation cipher with the specified key. + * + * @param plaintext the text to encrypt + * @param key the permutation key (array of integers representing positions) + * @return the encrypted text + * @throws IllegalArgumentException if the key is invalid + */ + public String encrypt(String plaintext, int[] key) { + validateKey(key); + + if (plaintext == null || plaintext.isEmpty()) { + return plaintext; + } + + // Remove spaces and convert to uppercase for consistent processing + String cleanText = plaintext.replaceAll("\\s+", "").toUpperCase(); + + // Pad the text to make it divisible by key length + String paddedText = padText(cleanText, key.length); + + StringBuilder encrypted = new StringBuilder(); + + // Process text in blocks of key length + for (int i = 0; i < paddedText.length(); i += key.length) { + String block = paddedText.substring(i, Math.min(i + key.length, paddedText.length())); + encrypted.append(permuteBlock(block, key)); + } + + return encrypted.toString(); + } + + /** + * Decrypts the given ciphertext using the permutation cipher with the specified key. + * + * @param ciphertext the text to decrypt + * @param key the permutation key (array of integers representing positions) + * @return the decrypted text + * @throws IllegalArgumentException if the key is invalid + */ + public String decrypt(String ciphertext, int[] key) { + validateKey(key); + + if (ciphertext == null || ciphertext.isEmpty()) { + return ciphertext; + } + + // Create the inverse permutation + int[] inverseKey = createInverseKey(key); + + StringBuilder decrypted = new StringBuilder(); + + // Process text in blocks of key length + for (int i = 0; i < ciphertext.length(); i += key.length) { + String block = ciphertext.substring(i, Math.min(i + key.length, ciphertext.length())); + decrypted.append(permuteBlock(block, inverseKey)); + } + + // Remove padding characters from the end + return removePadding(decrypted.toString()); + } + /** + * Validates that the permutation key is valid. + * A valid key must contain all integers from 1 to n exactly once, where n is the key length. + * + * @param key the permutation key to validate + * @throws IllegalArgumentException if the key is invalid + */ + private void validateKey(int[] key) { + if (key == null || key.length == 0) { + throw new IllegalArgumentException("Key cannot be null or empty"); + } + + Set keySet = new HashSet<>(); + for (int position : key) { + if (position < 1 || position > key.length) { + throw new IllegalArgumentException("Key must contain integers from 1 to " + key.length); + } + if (!keySet.add(position)) { + throw new IllegalArgumentException("Key must contain each position exactly once"); + } + } + } + + /** + * Pads the text with padding characters to make its length divisible by the block size. + * + * @param text the text to pad + * @param blockSize the size of each block + * @return the padded text + */ + private String padText(String text, int blockSize) { + int remainder = text.length() % blockSize; + if (remainder == 0) { + return text; + } + + int paddingNeeded = blockSize - remainder; + StringBuilder padded = new StringBuilder(text); + for (int i = 0; i < paddingNeeded; i++) { + padded.append(PADDING_CHAR); + } + + return padded.toString(); + } + /** + * Applies the permutation to a single block of text. + * + * @param block the block to permute + * @param key the permutation key + * @return the permuted block + */ + private String permuteBlock(String block, int[] key) { + if (block.length() != key.length) { + // Handle case where block is shorter than key (shouldn't happen with proper padding) + block = padText(block, key.length); + } + + char[] result = new char[key.length]; + char[] blockChars = block.toCharArray(); + + for (int i = 0; i < key.length; i++) { + // Key positions are 1-based, so subtract 1 for 0-based array indexing + result[i] = blockChars[key[i] - 1]; + } + + return new String(result); + } + + /** + * Creates the inverse permutation key for decryption. + * + * @param key the original permutation key + * @return the inverse key + */ + private int[] createInverseKey(int[] key) { + int[] inverse = new int[key.length]; + + for (int i = 0; i < key.length; i++) { + // The inverse key maps each position to where it should go + inverse[key[i] - 1] = i + 1; + } + + return inverse; + } + + /** + * Removes padding characters from the end of the decrypted text. + * + * @param text the text to remove padding from + * @return the text without padding + */ + private String removePadding(String text) { + if (text.isEmpty()) { + return text; + } + + int i = text.length() - 1; + while (i >= 0 && text.charAt(i) == PADDING_CHAR) { + i--; + } + + return text.substring(0, i + 1); + } + + /** + * Gets the padding character used by this cipher. + * + * @return the padding character + */ + public char getPaddingChar() { + return PADDING_CHAR; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java b/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java index f81252980468..2ccdc866ad4c 100644 --- a/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java @@ -1,147 +1,147 @@ -package com.thealgorithms.ciphers; - -import java.util.Arrays; - -/** - * The rail fence cipher (also called a zigzag cipher) is a classical type of transposition cipher. - * It derives its name from the manner in which encryption is performed, in analogy to a fence built with horizontal rails. - * https://en.wikipedia.org/wiki/Rail_fence_cipher - * @author https://github.com/Krounosity - */ - -public class RailFenceCipher { - - // Encrypts the input string using the rail fence cipher method with the given number of rails. - public String encrypt(String str, int rails) { - - // Base case of single rail or rails are more than the number of characters in the string - if (rails == 1 || rails >= str.length()) { - return str; - } - - // Boolean flag to determine if the movement is downward or upward in the rail matrix. - boolean down = true; - // Create a 2D array to represent the rails (rows) and the length of the string (columns). - char[][] strRail = new char[rails][str.length()]; - - // Initialize all positions in the rail matrix with a placeholder character ('\n'). - for (int i = 0; i < rails; i++) { - Arrays.fill(strRail[i], '\n'); - } - - int row = 0; // Start at the first row - int col = 0; // Start at the first column - - int i = 0; - - // Fill the rail matrix with characters from the string based on the rail pattern. - while (col < str.length()) { - // Change direction to down when at the first row. - if (row == 0) { - down = true; - } - // Change direction to up when at the last row. - else if (row == rails - 1) { - down = false; - } - - // Place the character in the current position of the rail matrix. - strRail[row][col] = str.charAt(i); - col++; // Move to the next column. - // Move to the next row based on the direction. - if (down) { - row++; - } else { - row--; - } - - i++; - } - - // Construct the encrypted string by reading characters row by row. - StringBuilder encryptedString = new StringBuilder(); - for (char[] chRow : strRail) { - for (char ch : chRow) { - if (ch != '\n') { - encryptedString.append(ch); - } - } - } - return encryptedString.toString(); - } - // Decrypts the input string using the rail fence cipher method with the given number of rails. - public String decrypt(String str, int rails) { - - // Base case of single rail or rails are more than the number of characters in the string - if (rails == 1 || rails >= str.length()) { - return str; - } - // Boolean flag to determine if the movement is downward or upward in the rail matrix. - boolean down = true; - - // Create a 2D array to represent the rails (rows) and the length of the string (columns). - char[][] strRail = new char[rails][str.length()]; - - int row = 0; // Start at the first row - int col = 0; // Start at the first column - - // Mark the pattern on the rail matrix using '*'. - while (col < str.length()) { - // Change direction to down when at the first row. - if (row == 0) { - down = true; - } - // Change direction to up when at the last row. - else if (row == rails - 1) { - down = false; - } - - // Mark the current position in the rail matrix. - strRail[row][col] = '*'; - col++; // Move to the next column. - // Move to the next row based on the direction. - if (down) { - row++; - } else { - row--; - } - } - - int index = 0; // Index to track characters from the input string. - // Fill the rail matrix with characters from the input string based on the marked pattern. - for (int i = 0; i < rails; i++) { - for (int j = 0; j < str.length(); j++) { - if (strRail[i][j] == '*') { - strRail[i][j] = str.charAt(index++); - } - } - } - - // Construct the decrypted string by following the zigzag pattern. - StringBuilder decryptedString = new StringBuilder(); - row = 0; // Reset to the first row - col = 0; // Reset to the first column - - while (col < str.length()) { - // Change direction to down when at the first row. - if (row == 0) { - down = true; - } - // Change direction to up when at the last row. - else if (row == rails - 1) { - down = false; - } - // Append the character from the rail matrix to the decrypted string. - decryptedString.append(strRail[row][col]); - col++; // Move to the next column. - // Move to the next row based on the direction. - if (down) { - row++; - } else { - row--; - } - } - - return decryptedString.toString(); - } -} +package com.thealgorithms.ciphers; + +import java.util.Arrays; + +/** + * The rail fence cipher (also called a zigzag cipher) is a classical type of transposition cipher. + * It derives its name from the manner in which encryption is performed, in analogy to a fence built with horizontal rails. + * https://en.wikipedia.org/wiki/Rail_fence_cipher + * @author https://github.com/Krounosity + */ + +public class RailFenceCipher { + + // Encrypts the input string using the rail fence cipher method with the given number of rails. + public String encrypt(String str, int rails) { + + // Base case of single rail or rails are more than the number of characters in the string + if (rails == 1 || rails >= str.length()) { + return str; + } + + // Boolean flag to determine if the movement is downward or upward in the rail matrix. + boolean down = true; + // Create a 2D array to represent the rails (rows) and the length of the string (columns). + char[][] strRail = new char[rails][str.length()]; + + // Initialize all positions in the rail matrix with a placeholder character ('\n'). + for (int i = 0; i < rails; i++) { + Arrays.fill(strRail[i], '\n'); + } + + int row = 0; // Start at the first row + int col = 0; // Start at the first column + + int i = 0; + + // Fill the rail matrix with characters from the string based on the rail pattern. + while (col < str.length()) { + // Change direction to down when at the first row. + if (row == 0) { + down = true; + } + // Change direction to up when at the last row. + else if (row == rails - 1) { + down = false; + } + + // Place the character in the current position of the rail matrix. + strRail[row][col] = str.charAt(i); + col++; // Move to the next column. + // Move to the next row based on the direction. + if (down) { + row++; + } else { + row--; + } + + i++; + } + + // Construct the encrypted string by reading characters row by row. + StringBuilder encryptedString = new StringBuilder(); + for (char[] chRow : strRail) { + for (char ch : chRow) { + if (ch != '\n') { + encryptedString.append(ch); + } + } + } + return encryptedString.toString(); + } + // Decrypts the input string using the rail fence cipher method with the given number of rails. + public String decrypt(String str, int rails) { + + // Base case of single rail or rails are more than the number of characters in the string + if (rails == 1 || rails >= str.length()) { + return str; + } + // Boolean flag to determine if the movement is downward or upward in the rail matrix. + boolean down = true; + + // Create a 2D array to represent the rails (rows) and the length of the string (columns). + char[][] strRail = new char[rails][str.length()]; + + int row = 0; // Start at the first row + int col = 0; // Start at the first column + + // Mark the pattern on the rail matrix using '*'. + while (col < str.length()) { + // Change direction to down when at the first row. + if (row == 0) { + down = true; + } + // Change direction to up when at the last row. + else if (row == rails - 1) { + down = false; + } + + // Mark the current position in the rail matrix. + strRail[row][col] = '*'; + col++; // Move to the next column. + // Move to the next row based on the direction. + if (down) { + row++; + } else { + row--; + } + } + + int index = 0; // Index to track characters from the input string. + // Fill the rail matrix with characters from the input string based on the marked pattern. + for (int i = 0; i < rails; i++) { + for (int j = 0; j < str.length(); j++) { + if (strRail[i][j] == '*') { + strRail[i][j] = str.charAt(index++); + } + } + } + + // Construct the decrypted string by following the zigzag pattern. + StringBuilder decryptedString = new StringBuilder(); + row = 0; // Reset to the first row + col = 0; // Reset to the first column + + while (col < str.length()) { + // Change direction to down when at the first row. + if (row == 0) { + down = true; + } + // Change direction to up when at the last row. + else if (row == rails - 1) { + down = false; + } + // Append the character from the rail matrix to the decrypted string. + decryptedString.append(strRail[row][col]); + col++; // Move to the next column. + // Move to the next row based on the direction. + if (down) { + row++; + } else { + row--; + } + } + + return decryptedString.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/WordLadder.java b/src/main/java/com/thealgorithms/strings/WordLadder.java index 665e5ff3220d..d59b8c3d04c0 100644 --- a/src/main/java/com/thealgorithms/strings/WordLadder.java +++ b/src/main/java/com/thealgorithms/strings/WordLadder.java @@ -1,4 +1,5 @@ package com.thealgorithms.strings; + // Modified by Danielle Ihm for assignment import java.util.Collection; import java.util.HashSet;