Columnar Transposition decryption algorithm in Java - java

Is anyone able to give the reverse/decryption algorithm for this Columnar cipher? The key length may vary, but here it is given as 4 for the encryption.
String input = "Hello World"
String output = "Hore llWdlo"
int key =4;
public static String encrypt(int key, String plainT) {
String outString= "";
for (int j = 0; j < key; j++) {
for (int i = j; i < plainT.length(); i += key) {
outString+= plainT.charAt(i);
}
}
return outString;
}

Java Strings are immutable, so you can't operate on their data. But if you could rewrite your encoding function to use an array of chars:
public static String encrypt(int key, String plainT)
{
char[] res = new char[plainT.length()];
int k = 0;
for (int j = 0; j < key; j++) {
for (int i = j; i < plainT.length(); i += key) {
res[k++] = plainT.charAt(i);
}
}
return String.copyValueOf(res);
}
Then you can easily revert the process:
public static String decrypt(int key, String encT)
{
char[] res = new char[encT.length()];
int k = 0;
for (int j = 0; j < key; j++) {
for (int i = j; i < encT.length(); i += key) {
res[i] = encT.charAt(k++);
}
}
return String.copyValueOf(res);
}
Concatenation is implemented here with an auxiliary char array and an additional index k. Reverting the process is then the same as reverting the indices. (This means that the decrypted array is not populated in order, just like the original string is not read from in order on encrypting.)
(Caveat: I'm not familiar with Java, so I hope that changing the char array and converting it to a string works as I expect and also that I haven't told any outright nonsense about Java Strings.)
Addendum You can also use your original concatenation code as base, but it will be a bit more complicated. Your column-wise encryption can be represented as:
H o r
e _ l
l W d
l o
The encrypted string is read horizontally, the decrypted original string vertically. If your string were "Hello World!", with an excamation mark, you'd have a string of length 12 and you could use your original code with a reverted key of 12 / 4 == 3.
But we actually have a variable key: 3 for the first lines and 2 for the last line.
public static String decrypt(int key, String str)
{
String out = "";
int skip = str.length() / key;
int rest = str.length() % key * (skip + 1);
for (int j = 0; j < skip; j++) {
int i = j;
while (i < str.length()) {
out += str.charAt(i);
i += skip;
if (i < rest) i++;
}
}
for (int i = skip; i < rest; i += skip + 1) {
out += str.charAt(i);
}
return out;
}
The inner loop now has a key of skip or skip + 1, depending on the region of the string. The last column (of the sketch above) is treated in a separate loop, because it looked tidier.

Related

Why am I getting an array out of bounds exception/no console output with cipher?

I can't see a clear reason why I'm getting an Array out of Bounds at this line:
matrix[row][col++] = plainText.charAt(i);
Could anyone elucidate me here? I get the out of bounds exception when I replace this:
cipherText = Arrays.deepToString(matrix);
with a sysout of the matrix. With the original code in place I simply get no console output.
Thanks for looking, I'm rather lost if that wasn't glaringly apparent.
import java.util.*;
public class RailFenceCypher {
public String encrypt(String plainText, int key) {
String cipherText = null;
boolean check = false;
// The 2d array to store the encrypted/decrypted text
char[][] matrix = new char[key][plainText.length()];
// Filling in the blank spaces in the array
for (int i = 0; i < key; i++) {
for (int j = 0; j < plainText.length(); j++) {
matrix[i][j] = '\n';
// Checking whether going up or down in the array, reversing if at top or bottom
int row = 0;
int col = 0;
for (int k = 0; k < plainText.length(); k++) {
if (row == 0 || row == key - 1) {
check = !check;
}
// entering the char into the row
matrix[row][col++] = plainText.charAt(i);
// finding the next row using check for direction
if (check) {
row++;
} else
row--;
}
}
for (int j = 0; j < key; j++) {
for (int m = 0; m < matrix.length; m++) {
if (matrix[j][m] != '\n') {
cipherText = Arrays.deepToString(matrix);
}
}
}
}
return cipherText;
}
}
I doubt that matrix.length() returns the length of [m], so your last for loops runs over the end of the array.
You possibly wanted to use plainText.length() instead as you did in the two for loops above.

Longest Most Common Substring Based on Whole-Word Phrases

I've been doing a lot of research around this topic and can't quite crack this one easily. There are a lot of valuable solutions I've come across online for solving this problem based on characters, but how would you solve this problem based on whole-word phrases to avoid the result returning a phrase that contains a partial word at the start or end of the phrase?
For example, given an Array of Strings, the output would be the most common whole-word phrase that is contained in most (not all) of the Strings within the Array.
This example below is the closest I've found so far but it only works about half of the time and includes partial word results which isn't quite what I'm after. I'm sure someone has solved this one before.
// function to find the stem (longest common
// substring) from the string array
public static String findstem(String arr[])
{
// Determine size of the array
int n = arr.length;
// Take first word from array as reference
String s = arr[0];
int len = s.length();
String res = "";
for (int i = 0; i < len; i++) {
for (int j = i + 1; j <= len; j++) {
// generating all possible substrings
// of our reference string arr[0] i.e s
String stem = s.substring(i, j);
int k = 1;
for (k = 1; k < n; k++)
// Check if the generated stem is
// common to all words
if (!arr[k].contains(stem))
break;
// If current substring is present in
// all strings and its length is greater
// than current result
if (k == n && res.length() < stem.length())
res = stem;
}
}
return res;
}
// Driver Code
public static void main(String args[])
{
String arr[] = { "grace", "graceful", "disgraceful",
"gracefully" };
String stems = findstem(arr);
System.out.println(stems);
}
Does this do what you intended. It simply checks to see if any word is a substring of itself and others.
If you want to check for real word substrings you would need to reference some dictionary which would be very time consuming.
String arr[] = { "grace", "graceful", "disgraceful",
"gracefully" };
String save = "";
int count = 0;
for (int i = 0; i < arr.length && count != arr.length; i++) {
count = 0;
for (int k = 0; k < arr.length; k++) {
if (arr[k].contains(arr[i])) {
count++;
save = arr[i];
}
}
}
System.out.println(save);

Putting all 8-bit binary sequences into a string array Java

what I am trying to put all possible 256 binary bit sequences into a string array. In order to do that, I have created 8 for loops to have all the possible cases. Here's what I've tried so far.
static String[] BitSequences() {
int[] result = new int[256];
for (int a = 0; a < 256; a++) {
for (int i = 0; i < 2; i++){
for (int j = 0; j < 2; j++){
for (int k = 0; k < 2; k++){
for (int l = 0; l < 2; l++){
for (int m = 0; m < 2; m++){
for (int n = 0; n < 2; n++){
for (int o = 0; o < 2; o++){
for (int p = 0; p < 2; p++){
result[a] = ; //this part is a problem
}
}
}
}
}
}
}
}
}
String str = Arrays.toString(result);
System.out.println(str);
return str;
}
This method is supposed to return a string array that contains all the possible cases. However, I don't know how to insert these value by making for-loops using int values. It is easy to print it out:
'
System.out.println(i+j+k+.....+p)
'
any help would be appreciated!
Consider using the built in conversion method for binary strings:
static String[] BitSequences() {
String[] result = new String[256];
for (int a = 0; a < 256; a++) {
result[a] = Integer.toBinaryString(a);
}
String str = Arrays.toString(result);
System.out.println(str);
return str;
}
An 8-bit, two's complement integer ranges from -128 to 127. To represent that range, we can use IntStream#rangeClosed.
From this answer, we can utilize BigInteger to left-pad the binary String (generated by Integer#toBinaryString) with 0s if its length is less than 8 (denoting that the value is positive).
Otherwise, the value represents a negative number, and its respective binary string will have a length greater than 8, which must be truncated to 8 characters using String#substring.
Finally, the Stream<String> can be collected to a String[] using Stream#toArray.
public static String[] generateBitSequences() {
return IntStream.rangeClosed(-128, 127)
.mapToObj(Integer::toBinaryString)
.map(BigInteger::new)
.map(b -> String.format("%08d", b)) // Left pad positive values with 0s.
.map(s -> s.substring(s.length() - 8)) // Remove leading 1s from negative values.
.toArray(String[]::new);
}
Output:
[10000000, 10000001, ..., 01111110, 01111111]

First repeated string java

OK, i know this question was asked many times here, but i still don't get it, how to find the first repeated character in a string ?
I did something which was close, but it gave me all repeated characters instead of only the first one.
Here's what i did :
private static void stringChar(){
String s = "sababa";
int count = 0;
char c[] = s.toCharArray();
System.out.println("Duplicate characters are :");
for(int i = 0; i < s.length(); i++){
for(int j = i + 1; j < s.length(); j++){
if(c[i] == c[j]) {
System.out.println(c[j]);
count++;
break;
}
}
}
}
One quick and dirty (yet effective) approach which comes to mind is to maintain a map whose keys are the characters. In this case, we don't even need a formal map, because we can use an integer array which maps to the underlying ASCII values of the characters.
The basic idea here is to walk down the string, and check the array if we have seen it before. If so, then print that character and exit.
int[] nums = new int[128]; // assuming only basic ASCII characters
String str = "stuff";
for (int i=0; i < str.length(); ++i) {
int index = str.charAt(i);
if (nums[index] != 0) {
System.out.println("The first repeated character is: " + str.charAt(i));
break;
}
++nums[index];
}
Demo
You need to break the outer loop if you have already found the repeating character.
boolean found = false;
for(int i = 0; i < s.length(); i++){
for(int j = i + 1; j < s.length(); j++){
if(c[i] == c[j]) {
System.out.println(c[j]);
found = true;
break;
}
}
if (found) {
break;
}
}
If you want the count of it
int count = 0;
char repeatingChar = '0'; //dummy to overcome compiler warning
for(int i = 0; i < s.length(); i++){
for(int j = i + 1; j < s.length(); j++){
if(c[i] == c[j]) {
repeatingChar = c[j];
count++;
}
}
if (count > 0) {
System.out.println("Repeating char is " + repeatingChar + ". Occurred " + count + " times");
break;
}
}
The first repeated char is one you have seen before as you iterate the chars in the string. You can keep track of the first time you see a char with a Boolean array. The array is indexed with the char value. Java automatically "widens" char to int. So, for each char, check if it's been seen before and if not, mark that it has been seen.
String s = "sababa";
boolean[] seenChars = new boolean[Character.MAX_VALUE + 1]; // initialized to all false.
for (char c : s.toCharArray()) {
if (Character.isSurrogate(c)) throw new IllegalArgumentException("No first char seen before seeing a surrogate. This algorithm doesn't work for codepoints needing surrogates in UTF-16.");
if (seenChars[c]) {
System.out.println("First repeated char is: " + c);
break;
}
seenChars[c] = true;
}
You might notice that I've been saying char instead of character. Character is not a well-defined term. Java's text datatypes use the UTF-16 encoding of the Unicode character set. Some Unicode codepoints require two UTF-16 code units (char), which are sometimes called surrogate pairs. Since they are not all unique as individual chars, the algorithm doesn't work if they are present. For example, try String s = "sab🚲aba"; (You can also write it as "sab\uD83D\uDEB2aba".) The answer would be "a", again. But now try String s = "sab🚲🚶aba"; (You can also write it as "sab\uD83D\uDEB2\uD83D\uDEB6aba".) The answer should still be "a" but the algorithm would say "\uD83D" (which of course can't be displayed because it is only part of a codepoint).
Short and Simple >
void findFirstDupChar() {
String s = "google";
Integer dupIndex = s.length() - 1;
boolean isDuplicateExists = false;
Map<Character, Integer> map = new LinkedHashMap<>();
char[] words = s.toCharArray();
for (int i = 0; i < words.length; i++) {
Integer index = map.put(words[i], i);
if (index != null) {
if (index < dupIndex) {
dupIndex = index;
isDuplicateExists = true;
}
}
}
System.out.println(isDuplicateExists ? words[dupIndex] + ", index=" + dupIndex : "No duplicateds found");
}
This will print output as following >
g, index=0

Java Create Strings Dynamically With Loops

I need to create a program that creates strings dynamically with some sort of loop (for/while). It would start out as a single-character string with an ASCII value of 1, than 2, than 3, and so on, until is reached 128. Once it reached 128, it would be a two-character string, with the first character of an ASCII value of 1, and the second character being 1. It would then be 1;1, 1;2, 1;3, until the second digit reached 128, and then which the first character would have a value of 2. How is this logically possible?
Here's what I tried so far:
for (int i = 0; i < 128; i++) {
str = ((char) i) + "";
for (int j = 0; j < 128; j++) {
str += ((char) j) + "";
//workWithString(str);
System.out.println(str);
str = str.substring(0, str.length() - 1);
}
}
And this works, but only for 2 digits. I would like for it to work with up to 32 digits. Is there any easier way to accomplish this without having 32 for loops?
Thanks!
Sorry, guys. Figured it out on my own. For anyone who's interested in the answer, I achieved it like so:
public static void addCharacter(String str, int depth) {
for (int j = 33; j < 127; j++) {
for (int i = 0; i < depth; i++) {
str += ((char) j) + "";
System.out.println(str);
addCharacter(str, depth - 1);
str = str.substring(0, str.length() - 1);
}
}
}
Where depth is the amount of digits you want it to calculate, and str is the string you want to add a character to.
public class StringCreator {
public static void main(String[] args) {
for(int i=0;i<=128;i++){
for(j=0;j<10;j++){
System.out.println(i+"."+j);
}
}
}
}

Categories

Resources