Vignere Cipher encryption/decryption using arrays - java

I need to first encrypt and then decrypt a message using the Vigenere cypher. This is how it should work
example message: "c a t s _ d o g s"
keyword "rats": r a t s r a t s r
order of the letter in the message (start at a=0):2 0 19 18 () 3 14 6 18
order of the letter in the keyword: 17 0 19 18 17 0 19 18 17
sum of the two orders: 19 0 38 36 17 3 39 24 35
new letter for the message* t a m k d h y j
encrypted message = "tamk uoyk"
Note: if the sum > 26 then we subtract 26 from the sum to get a cyclical alphabet. Example:
z + b = 25 + 1 = 26; 26 - 26 = 0 --> a
I have written the methods to obtain the numerical value of the keyword, and also two methods that "add" or "subtract" individual letters and two methods that perform the caesar encoding/decoding (simply shifting the entire message by an int to the right in the alphabet, or to the left to decrypt).
The piece that I really need help for is to how to create a for loop that's going to repeat the keyword the appropriate amount of times (to have the same length as the message) and proceed to the obtainKeys method to get the numerical values of the repeated key.
Here is my entire program; the part I am struggling with is at the end (Q2f)
import java.util.Arrays;
public class Cypher {
public static void main(String[] args) {
System.out.println(charRightShift('z', 3));
System.out.println(charLeftShift('z', 3));
String test = caesarEncode("cats and dogs", 5);
System.out.println(test);
System.out.println(caesarDecode(test, 5));
obtainKeys("abcxyz");
System.out.println(vigenereEncode("elephants", "rats"));
}
//Q2a-b
//Generalized method for char shifts
public static char charShift(char c, int n) {
//value of n should be between 0 and 25
if (Math.abs(n) < 0 || 25 < Math.abs(n)) {
//returning the ascii value of '0' which
//is nul & adding error message
int zero = 0;
c = (char) zero;
throw new IllegalArgumentException("n has to be 0<=|n|<=25");
}
//character c should be a lower case latin letter
//if not, we simply return c, the original character,
//skipping this else if
else if (c >= 'a' && c <= 'z') {
c = (char) (c + n);
if (c > 'z') {
c = (char) (c - 26);
} else if (c < 'a') {
c = (char) (c + 26);
}
}
return c;
}
//method that shifts the value of the character to the right
public static char charRightShift(char c, int n) {
c = charShift(c, n);
return c;
}
//method that shifts the value of the character to the left
public static char charLeftShift(char c, int n) {
n = -n;
c = charShift(c, n);
return c;
}
//Q2c
//method that shifts the message to the right by int 'key' characters
public static String caesarEncode(String message, int key) {
//transform string into char array
char[] messageEncrypt = message.toCharArray();
//for each char, we shift it by 'key' ints,
//using charRightShift method
for (int i = 0; i < messageEncrypt.length; i++) {
char c = messageEncrypt[i];
c = charRightShift(c, key);
messageEncrypt[i] = c;
}
return new String(messageEncrypt);
}
//Q2d
//method that shifts the message to the left by int 'key' characters
public static String caesarDecode(String message, int key) {
//transform string into char array
char[] messageDecrypt = message.toCharArray();
//for each char, we shift it by 'key' ints using charLeftShift
for (int i = 0; i < messageDecrypt.length; i++) {
char c = messageDecrypt[i];
c = charLeftShift(c, key);
messageDecrypt[i] = c;
}
return new String(messageDecrypt);
}
//Q2e
//method to obtain the int array storing the numerical value of the String
public static int[] obtainKeys(String s) {
//creating int array where we're going to
//store the numerical value of the String
int[] keys = new int[s.length()];
int j;
//for each ascii value of the char in string s, we substract 97 to
//get the lower case english alphabet character corresponding to it
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
j = c - 97;
//now store every int in the int array
keys[i] = j;
}
String keysString = Arrays.toString(keys);
return keys;
}
//Q2f
public static String vigenereEncode(String message, String keyword) {
//for loop check if there are any 'illegal' characters in the keyword
char[] kword = keyword.toCharArray();
for (int i = 0; i < kword.length; i++) {
char c = kword[i];
if (c < 'a' || c > 'z') {
throw new IllegalArgumentException(
"The keyword must only contain characters " +
"from the lower case English alphabet.");
}
}
int[] numMessage = obtainKeys(message);
int[] numKeyword = obtainKeys(keyword);
for (int i = 0; i < message.length(); i++) {
for (int j = 0; j < keyword.length(); i++) {
//NOT SURE IF I NEED A NESTED LOOP HERE
//WHAT TO DO HERE?
}
}
return messageVigenere;
}
}

You can do this by using the mod operation %.
char[] messageArray = message.toCharArray();
char[] encryptedMessage = new char[messageArray.length];
int[] numKeyword = obtainKeys(keyword);
int keywordLength = numKeyword.length;
for(int i=0; i<message.length(); i++){
int shiftAmount = numKeyword[i % keywordLength];
char c = messageArray[i];
c = charRightShift(c,shiftAmount);
encryptedMessage[i] = c;
}

Instead of repeating the keyword til it is the length of the message, you can find which letter is needed using modulus logic. For any location n in the message, the keyword letter is keyword[n % keyword.length()].

Related

find the correct algorithme to find all the possible binary combination

I'm trying to write a non-recursive Java method called showStar, which takes a string, and generates ALL possible combinations of that string without the mask “*” characters.
receiving this as an input "1011*00*10",
the method `showStar` will display output like:
1011000010
1011000110
1011100010
1011100110
I tried this way, however, as soon as the number of possible cases is more than the String length, the output is not exact.
Here is my code.
public static void showStar(String s){
String save;
int count = 0;
int poss;
save = s.replace('*','0');
StringBuilder myString = new StringBuilder(save);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '*' && myString.charAt(i) == '0') {
myString.setCharAt(i, '1');
System.out.println(myString);
}
}
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '*' && myString.charAt(i) == '1') {
myString.setCharAt(i, '0');
System.out.println(myString);
}
}
}
Say there are k *s. Then there are 2^k solutions. You can generate these by copying the bits from the integers 0 - 2^k-1 in order. (adding sufficient leading zeroes)
E.g. 1**1:
0 = 00 => 1001
1 = 01 => 1011
2 = 10 => 1101
3 = 11 => 1111
Here a recursive algoritm works just perfectly:
You check if an input string contains an asterisk '*' by using an x = str.indexOf('*');
If no asterisk is present (x == -1), you just print the string and return
Otherwise, you replace the asterisk at the position to '0' and '1' and call showStar() recursively for both replacements
public static void showStar(String str) {
int x = str.indexOf('*');
if(x == -1) {
System.out.println(str);
return;
}
String prefix = str.substring(0, x);
String suffix = str.substring(x + 1);
for (char i = '0'; i <= '1'; i++) {
showStar(prefix + i + suffix);
}
}
Update
In non-recursive implementation we need to collect the asterisk positions, then prepare a binary representation and set appropriate bits at the known positions:
public static void showStar(String str) {
int[] xs = IntStream.range(0, str.length())
.filter(i -> str.charAt(i) == '*')
.toArray();
int num = (int) Math.pow(2, xs.length); // 2^n variants for n asterisks
String format = xs.length > 0 ? "%" + xs.length + "s" : "%s"; // fix if no '*'
for (int i = 0; i < num; i++) {
String bin = String.format(format, Integer.toBinaryString(i))
.replace(' ', '0'); // pad leading zeros
StringBuilder sb = new StringBuilder(str);
// set 0 or 1 in place of asterisk(s)
for (int j = 0; j < xs.length; j++) {
sb.setCharAt(xs[j], bin.charAt(j));
}
System.out.println(sb);
}
}

Problem encryption exercise with ASCII table

I have a problem with this exercise.
In this exercise I enter a word and a number. I have to use the ASCII table to encrypt the word.
If I enter the letter "a" and the number 3, the letter becomes "d".
If I enter the letter "z" and the number 2, the letter should become "b", but a symbol comes out.
Another problem is if I use an uppercase letter. If I enter the uppercase letter "Z" I'll still get a symbol.
Another problem is if I use the letters "aB" and the number -2 should come out "yZ", but symbols come out.
This is the exercise:
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
String s, n="";
int N;
System.out.println("Insert a word");
s=in.next();
System.out.println("Insert a number");
N=in.nextInt();
n=uno(s, N);
System.out.println("That's the encrypted word: " + n);
}
public static String uno (String s, int N) {
String f, n="";
int c;
int length = s.length();
for (int i=0; i<length; i++) {
c = s.charAt(i);
c=c+N;
f = Character.toString((char)c);
n=n+f;
}
return n;
}
ASCII for z is 122. You add 2 to that. The ASCII for 124 is | symbol.
You need to check if your addition is going out of range (i.e. above 122).
Note: this won't work is N is greater than 26. Check the solution just below that implements modulo to handle that.
public static String uno (String s, int N) {
String f, n = "";
int c;
int length = s.length();
for (int i = 0; i < length; i++) {
c = s.charAt(i);
c = c + N;
if (c >= 122) {
c -= 26;
}
f = Character.toString((char) c);
n = n + f;
}
return n;
}
Side note: Never concatenate a string in a loop using +. It is very inefficient. Using StringBuilder.
Handle case sensitive letters concisely:
public static String uno (String s, int N) {
StringBuilder n = new StringBuilder();
int bound = s.length();
IntStream.range(0, bound).forEach(i -> {
char c = s.charAt(i);
n.append(Character.isUpperCase(c) ?
(char) ((c + N - 'A') % 26 + 'A') :
(char) ((c + N - 'a') % 26 + 'a'));
});
return n.toString();
}
Handling negative numbers:
public static String uno (String s, int N) {
StringBuilder n = new StringBuilder();
int bound = s.length();
IntStream.range(0, bound).forEach(i -> {
char c = s.charAt(i);
if (N > 0) {
n.append(Character.isUpperCase(c) ?
(char) ((c + N - 'A') % 26 + 'A') :
(char) ((c + N - 'a') % 26 + 'a'));
} else {
n.append((char) (c + N % 26 + 26));
}
});
return n.toString();
}
Check this comment for a good point on your naming conventions.

Occurrences in char Array

I have this problem,
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
at Exercise_12_2.main(Exercise_12_2.java:28)
When i am trying to just simply count the occurrences of the letters in a char array. I just cant seem to wrap my head around how to work it out. I have been at it for several hours. Please help get me on the right track.
input: a a a b b c !
Expected output:
Counts:
a 3
b 2
c 1
This is my code so far. Please help me.
import java.util.Scanner;
public class Exercise_12_2
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in); // Setup scanner
char[] charArray = new char[100];
int[] counts = new int[26];
char tempinput = '?';
System.out.print("Enter letters (or ! to quit): ");
while (tempinput != '!')
{
tempinput = (input.next()).charAt(0);
charArray[tempinput]++;
for (int c = 'a'; c <= 'z'; c++)
{
for (int k = 0; k <= charArray.length; k++)
{
if (c == charArray[k])
{
counts[c] += 1;
}
}
}
}
}
public static void displayCounts(int[] counts)
{
for (int i = 0; i < counts.length; i++)
{
if ((i + 1) % 10 == 0)
{
System.out.println(counts[i] + " " + (char)(i + 'a'));
}
else
{
System.out.print(counts[i] + " " + (char)(i + 'a') + " ");
}
}
}
}
java.lang.ArrayIndexOutOfBoundsException: 100 means you are asking for the 101st element of an array with only 100 elements. Remember, java arrays are indexed starting with 0.
From that you might be able to tell why this line is broken:
for (int k = 0; k <= charArray.length; k++)
charArray.length is 100, so you run up until k is 101, meaning that you try charArray[100] which is asking for the 101st element of charArray, but charArray only has 100 elements.
If you switch k <= charArray.length to k < charArray.length you should get farther.
On an unrelated note, counts[c] += 1; won't work. The int val of a char is its ascii value. The letter a for example is 97, so you'll go way off the end of your counts array. This also doesn't account for upper/lower case (which have different numeric values).
This problem becomes much easier if you use an appropriate data structure. For example, I would use a Map<Character, Integer> to keep the count and then iterate that to display. Like,
Scanner input = new Scanner(System.in); // Setup scanner
System.out.print("Enter letters (or ! to quit): ");
Map<Character, Integer> map = new HashMap<>();
char ch;
while ((ch = Character.toLowerCase(input.next().charAt(0))) != '!') {
map.put(ch, map.getOrDefault(ch, 0) + 1);
}
System.out.println("Counts:");
for (int c = 'a'; c <= 'z'; c++) {
if (map.containsKey((char) c)) {
System.out.printf("%c\t%d%n", c, map.get((char) c));
}
}
Which I tested with your example input, and I get as requested:
Enter letters (or ! to quit): a a a b b c !
Counts:
a 3
b 2
c 1
I think after creating the character array of [a, a, b c, d, !, ..], following code will be enough to calculate count for each alphabet [a-z].
for (int k = 0; k < charArray.length; k++)
{
char c = charArray[k];
if (c >= 'a' && c <= 'z' )
{
counts[c - 'a'] += 1;
}
}

How to use ASCII in array

I want to write a program that takes a string text, counts the appearances of every letter in English and stores them inside an array.and print the result like this:
java test abaacc
a:***
b:*
c:**
* - As many time the letter appears.
public static void main (String[] args) {
String input = args[0];
char [] letters = input.toCharArray();
System.out.println((char)97);
String a = "a:";
for (int i=0; i<letters.length; i++) {
int temp = letters[i];
i = i+97;
if (temp == (char)i) {
temp = temp + "*";
}
i = i - 97;
}
System.out.println(temp);
}
Writing (char)97 makes the code less readable. Use 'a'.
As 3kings said in a comment, you need an array of 26 counters, one for each letter of the English alphabet.
Your code should also handle both uppercase and lowercase letters.
private static void printLetterCounts(String text) {
int[] letterCount = new int[26];
for (char c : text.toCharArray())
if (c >= 'a' && c <= 'z')
letterCount[c - 'a']++;
else if (c >= 'A' && c <= 'Z')
letterCount[c - 'A']++;
for (int i = 0; i < 26; i++)
if (letterCount[i] > 0) {
char[] stars = new char[letterCount[i]];
Arrays.fill(stars, '*');
System.out.println((char)('a' + i) + ":" + new String(stars));
}
}
Test
printLetterCounts("abaacc");
System.out.println();
printLetterCounts("This is a test of the letter counting logic");
Output
a:***
b:*
c:**
a:*
c:**
e:****
f:*
g:**
h:**
i:****
l:**
n:**
o:***
r:*
s:***
t:*******
u:*

Hackerrank: Sherlock and Anagrams(Moderate under Strings section)

Problem description: https://www.hackerrank.com/challenges/sherlock-and-anagrams
Can somebody please tell me what am I doing wrong? My algorithm is:
Input the string ; str
Generate a pattern string from length i=1 to str.length-2
Check whether anagram of pattern string exist in str.substring(i+1)
Below are the test cases which are NOT passing :
input-string My OP Expected OP
ifailuhkqq 2 3
My code:
public class SherlockandAnagrams
{
static int count = 0;
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
generatePairs(sc.next());
int len = 1;
}
public static void generatePairs(String str)
{
int len = 1;
//int i=0;
while (len < str.length())
{
for (int i = 0; i + len <= str.length(); i++)
findAnagramPairs(str, len, str.substring(i, i + len), i + 1);
len++;
}
System.out.println(count);
}
private static void findAnagramPairs(String str, int len, String pattern, int p)
{
int i = p;
while (i + len <= str.length())
{
if (checkAnagram(pattern, str.substring(i, i + len)))
{
count++;
}
i++;
}
}
private static boolean checkAnagram(String pattern, String text)
{
if (pattern.length() == 1)
{
if (pattern.equals(text))
return true;
else
return false;
}
else
{
int i = 0;
int j = pattern.length() - 1;
while (i < pattern.length())
{
if (pattern.charAt(i) == text.charAt(j))
{
i++;
j--;
}
else
return false;
}
return true;
}
}
}
A simpler solution to the problem would be the following:
An anagramic pair with starting-indices at (n , m) and length l can only exist, if another pair with length l - 1 at (n or n - 1 or n + 1 , m or m - 1 or m - 1) exists. Thus we can easily reduce efficiency from bruteforce to a more efficient solution.
An example:
A B C B A A B C A
len 1 A A A A //all pairs containing A
len 2 A B B A //all pairs that match A B or its reverse
len 3 A B C B A //all pairs that match A B C or its reverse
OR
A B C B A A B C A
len 1 B B B //all pairs containing B
len 2 B C B B C //all pairs that match B C or its reverse
len 3 A B C B A A B C //all pairs that match A B C or its reverse
The same applies to any other pair of length l and it's matching pairs with length l + 1. In general, a pair of length l + 1 only exists, if two pairs (a , b) and (c , d) of length l exists, such that either a = c - l and b = d + l or a = c + l and b = d - l exist.
In pseudocode this would look like this:
set pairs = listAnagrams(input , 1)
int len = 1
while NOT pairs.isEmpty()
set next_len
//generate pairs with length len + 1
for pair p in pairs
pair left = pair(p.a - len , p.b + len)
pair right = pair(p.a + len , p.b - len)
if pairs.contains(left)
next_len.add(pair(p.a , left.b)
if pairs.contains(right)
next_len.add(pair(left.a , p.b)
pairs = next_len
++len

Categories