so I have a programming question. I want to be able to create a breakCaesarCipher class that does the following:
1) splitting the encrypted message
2) determining the two decryption keys
3) creating an instance of CaesarCipherTwo with those keys.
4) calling the decrypt method.
I don't know where to begin. I'm confused as to how one might split the encrypted message. In a previous lesson we did it like so:
import edu.duke.*;
public class CaesarBreaker2 {
public int[] countOccurrencesOfLetters(String message) {
//snippet from lecture
String alph = "abcdefghijklmnopqrstuvwxyz";
int[] counts = new int[26];
for (int k=0; k < message.length(); k++) {
char ch = Character.toLowerCase(message.charAt(k));
int dex = alph.indexOf(ch);
if (dex != -1) {
counts[dex] += 1;
}
}
return counts;
}
public int maxIndex(int[] values) {
int maxDex = 0;
for (int k=0; k < values.length; k++) {
if (values[k] > values[maxDex]) {
maxDex = k;
}
}
return maxDex;
}
public String decrypt(String encrypted) {
CaesarCipher cc = new CaesarCipher();
int[] freqs = countOccurrencesOfLetters(encrypted);
int maxDex = maxIndex(freqs);
int dkey = maxDex - 4;
if (maxDex < 4) {
dkey = 26 - (4-maxDex);
}
return cc.encrypt(encrypted,26-dkey);
}
public String halfOfString(String message, int start) {
StringBuilder halfString = new StringBuilder();
for (int index=start;index < message.length();index += 2) {
halfString.append(message.charAt(index));
}
return halfString.toString();
}
public int getKey(String s) {
int[] letterFreqs = countOccurrencesOfLetters(s);
int maxDex = maxIndex(letterFreqs);
int dkey = maxDex - 4;
if (maxDex < 4) {
dkey = 26 - (4-maxDex);
}
return 26-dkey;
}
public String decryptTwoKeys(String encrypted) {
String firstHalfEncrypted = halfOfString(encrypted,0);
String secondHalfEncrypted = halfOfString(encrypted,1);
int firstHalfKey = getKey(firstHalfEncrypted);
int secondHalfKey = getKey(secondHalfEncrypted);
CaesarCipherTwo ccT = new CaesarCipherTwo();
System.out.println("First key:\t" + firstHalfKey + "\nSecond key:\t"
+ secondHalfKey);
return ccT.encryptTwoKeys(encrypted,firstHalfKey,secondHalfKey);
}
public void testDecrypt() {
FileResource fileResource = new FileResource();
String encrypted = fileResource.asString();
System.out.println("Encrypted message:\n" + encrypted);
System.out.println("\nDecrypted message:\n" + decryptTwoKeys(encrypted));
String encrypted2 = "Aal uttx hm aal Qtct Fhljha pl Wbdl. Pvxvxlx!";
System.out.println("Encrypted message:\n" + encrypted2);
System.out.println("\nDecrypted message:\n" + decryptTwoKeys(encrypted2));
}
}
but I don't understand how to convert this code so it will work in this new class called TestCaesarCipherTwo. Here is the code for TestCaesarCipherTwo:
import edu.duke.*;
public class TestCaesarCipherTwo {
public int[] countOccurrencesOfLetters(String message) {
//snippet from lecture
String alph = "abcdefghijklmnopqrstuvwxyz";
int[] counts = new int[26];
for (int k=0; k < message.length(); k++) {
char ch = Character.toLowerCase(message.charAt(k));
int dex = alph.indexOf(ch);
if (dex != -1) {
counts[dex] += 1;
}
}
return counts;
}
public int maxIndex(int[] values) {
int maxDex = 0;
for (int k=0; k < values.length; k++) {
if (values[k] > values[maxDex]) {
maxDex = k;
}
}
return maxDex;
}
public String halfOfString(String message, int start) {
StringBuilder halfString = new StringBuilder();
for (int index=start;index < message.length();index += 2) {
halfString.append(message.charAt(index));
}
return halfString.toString();
}
public void simpleTests() {
FileResource fileResource = new FileResource();
String fileAsString = fileResource.asString();
CaesarCipherTwoKeys cctk = new CaesarCipherTwoKeys(17, 3);
String encrypted = cctk.encrypt(fileAsString);
System.out.println("Encrypted string:\n"+encrypted);
String decrypted = cctk.decrypt(encrypted);
System.out.println("Decrypted string:\n"+decrypted);
String blindDecrypted = breakCaesarCipher(encrypted);
System.out.println("Decrypted string using breakCaesarCipher():\n"+blindDecrypted);
}
public String breakCaesarCipher(String input) {
int[] freqs = countOccurrencesOfLetters(input);
int freqDex = maxIndex(freqs);
int dkey = freqDex - 4;
if (freqDex < 4) {
dkey = 26 - (4-freqDex);
}
CaesarCipherTwoKeys cctk = new CaesarCipherTwoKeys(dkey);
return cctk.decrypt(input);
}
}
WARNING: I also have a constructor error on this line CaesarCipherTwoKeys cctk = new CaesarCipherTwoKeys(dkey); stating CaesarCipherTwoKeys in class CaesarCipherTwoKeys cannot be applied to given types; required int,int; found int....
Can anyone show me how to change my code so that my breakCaesarCipher method splits the encrypted message, determines the two decryption keys used to encrypt the message, create an instance of CaesarCipherTwo with those two keys, and call the decrypt method? Any suggestions are welcome. Please let me know if you want more details into the nature of this problem.
Related
First Class which is giving me the error. I'm to make an array with the letters A-Z, and then have the user input the keys which it takes and compares each character's position in the array of the key with the position of a user inputted string
import java.util.*;
public class Encrypt{
private Square plain1;
private Square plain2;
private Square Encrypt1;
private Square Encrypt2;
public Encryption(String key1, String key2) {
Square plain1 = new Square();
Square plain2 = new Square();
Square Encrypt1= new Square(key1);
Square Encrypt2= new Square(key2);
}
public String encrypt(String msg) {
String EmpS = "";
String STR = "";
for(int i = 0; i < message.length(); i+=2){
char iMsg = message.charAt(i);
char iMsg2 = message.charAt(i+1);
int[] posRay = plain1.findPosition(iMsg);
int[] posRay2 = plain2.findPosition(iMsg2);
String answer = "" + Encrypt1.getChar(posRay[0], posRay2[1]);
String Combined = "" + answer;
String answer2 = "" + Encrypt2.getChar(posRay2[0], posRay[1]);
String Combined2 = "" + answer2;
String BothCom = Combined + Combined2;
STR = STR.concat(BothCom);
return STR;
}
return STR;
}
2nd class that is responsible for the array
public class Square {
private char[][] matrix;
public Square() {
arr= new char[5][5];
int ascii= 65;
for (int i = 0; i < 5; i++){
for(int j = 0; j < 5; j++){
arr[i][j] = (char) ascii;
ascii++;
}
}
}
}
public int[] findPosition(char Chart) {
int[] position= new int[2];
position[0] = -1;
popositions[1] = -1;
for (int i = 0; i < 5; i++){
for (int j = 0; i < 5; j++){
if(matrix[i][j] == Chart){
posistion[0] = i;
position[1] = j;
return position;
}
}
}
I'm getting this as an error (everything else works), what's the issue?:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Square.getPos(char)" because "this.plain1" is null
at findPosition.encrypt(Encrypt.java:45)
at IO.printResults(IO.java:101)
at Test.main(Proj8.java:26)
You are not assigning your class variables, you are actually assigning new local variables inside the method. Change to:
public Encryption(String key1, String key2) {
plain1 = new Square();
plain2 = new Square();
Encrypt1= new Square(key1);
Encrypt2= new Square(key2);
}
I am trying to separate a text in k-shingles, sadly I cannot use scanner. If the last shingle is too short, I want to fill up with "_". I came this far:
public class Projektarbeit {
public static void main(String[] args) {
testKShingling(7, "ddssggeezzfff");
}
public static void testKShingling(int k, String source) {
//first eliminate whitespace and then fill up with withespaces to match target.length%shingle.length() == 0
String txt = source.replaceAll("\\s", "");
//get shingles
ArrayList<String> shingles = new ArrayList<String>();
int i;
int l = txt.length();
String shingle = "";
if (k == 1) {
for(i = 0; i < l; i++){
shingle = txt.substring(i, i + k);
shingles.add(shingle);
};
}
else {
for(i = 0; i < l; i += k - 1){
try {
shingle = txt.substring(i, i + k);
shingles.add(shingle);
}
catch(Exception e) {
txt = txt.concat("_");
i -= k - 1;
};
};
}
System.out.println(shingles);
}
}
Output: [ddssgge, eezzfff, f______]
It works almost, but in the with the given parameters in the example the last shingle is not necessary (it should be [ddssgge, eezzfff]
Any idea how to do this more beautiful?
To make the code posted work you only need to add break and the end of the catch block:
catch(Exception e) {
txt = txt.concat("_");
i -= k - 1;
break;
};
Having said that I wouldn't use an Exception to control the program. Exception are just that: should be used for run time errors.
Avoid StringIndexOutOfBoundsException by controlling the loop parameters:
public static void main(String[] args) {
testKShingling(3, "ddssggeezzfff");
}
public static void testKShingling(int substringLength, String source) {
//todo validate input
String txt = source.replaceAll("\\s", "");
//get shingles
ArrayList<String> shingles = new ArrayList<>();
int stringLength = txt.length();
if (substringLength == 1) {
for(int index = 0; index < stringLength; index++){
String shingle = txt.substring(index, index + substringLength);
shingles.add(shingle);
};
}
else {
for(int index = 0; index < stringLength -1 ; index += substringLength - 1){
int endIndex = Math.min(index + substringLength, stringLength);
String shingle = txt.substring(index, endIndex);
if(shingle.length() < substringLength){
shingle = extend(shingle, substringLength);
}
shingles.add(shingle);
};
}
System.out.println(shingles);
}
private static String extend(String shingle, int toLength) {
String s = shingle;
for(int index = 0; index < toLength - shingle.length(); index ++){
s = s.concat("_");
}
return s;
}
An alternative implementation of testKShingling:
public static void testKShingling(int substringLength, String source) {
//todo validate input
String txt = source.replaceAll("\\s", "");
ArrayList<String> shingles = new ArrayList<>();
if (substringLength == 1) {
for(char c : txt.toCharArray()){
shingles.add(Character.toString(c));
};
}
else {
while(txt.length() > substringLength) {
String shingle = txt.substring(0, substringLength);
shingles.add(shingle);
txt = txt.substring(substringLength - 1); //remove first substringLength - 1 chars
}
if(txt.length() < substringLength){ //check the length of what's left
txt = extend(txt, substringLength);
}
shingles.add(txt); //add what's left
}
System.out.println(shingles);
}
So I have written a Java Program (Which I have attached the code for below) for a cryptographic cipher I designed (based on the SPN cipher but with a few modifications). Anyways, when I run my program, the code instantly stops and I receive a java.lang.ThreadDeath error. I have tried even adding a print statement to the very beginning (as in first line) of my main method (so it was the first thing processed by my program), but I had no luck, Java stopped before my print statement could even run.
Here is my code:
package SSPN;
import java.io.*;
import java.util.*;
public class Main throws IOException{
public static final int NUM_ROUNDS = 1;
public static final File f = new File("sBoxes.txt");
//Need better comments!!
//For each character, prints out a binary string of length 7. Does zero - padding!
public static String[] plaintextToCharacterBinarySequence(String plaintext)
{
//Getting int values
int[] charRepresentations = new int[plaintext.length()];
for (int i = 0; i < charRepresentations.length; i++)
{
char ch = plaintext.charAt(i);
int asciiValue = (int)ch;
charRepresentations[i] = asciiValue;
}
String[] binValues = new String[charRepresentations.length];
System.out.print("Binary values are: ");
for (int i = 0; i < binValues.length; i++)
{
String str = Integer.toBinaryString(charRepresentations[i]);
StringBuilder sb = new StringBuilder(str);
if (str.length() !=7)
{
int DIFF = 7 - str.length(); //As it is an Ascii Value, must be less than length 7
for (int j = 7; j > (7 - DIFF); j--)
{
sb.insert(0,"0");
}
}
str = sb.toString();
binValues[i] = str;
System.out.print(binValues[i] + " ");
}
System.out.println();
return binValues;
}
public static String binarySequenceToString(String[] sequence)
{
StringBuilder sb = new StringBuilder();
for (int i =0; i < sequence.length; i++)
{
int val = Integer.parseInt(sequence[i], 2);
char ch = (char) val;
sb.append(ch);
}
return sb.toString();
}
/*We define an instance of the affine cipher (for extra security), with a = length of message,b = ascii value of 1st character.
m = 128. Then, we convert the output to (zero-padded) 7-bit binary number, and store the result as the output of a hash
table (where the input binary number is the key)*/
public static HashMap<String, String> defineandStoreSBoxes(File f, int lengthOfMessage, int firstAsciiValue) throws FileNotFoundException, IOException
{
BufferedReader br = new BufferedReader(new FileReader(f));
HashMap<String, String> hm = new HashMap <String, String>();
String currentSBox;
while ((currentSBox = br.readLine()) != null)
{
int base10Val = Integer.parseInt(currentSBox, 2);
int encryptedOutput = lengthOfMessage * base10Val + firstAsciiValue; //(LIMITATION: Without modding by 128, encryptedOutput Cannot exceed Integer.MAX_VALUE)
String binOutput = Integer.toBinaryString(encryptedOutput);
StringBuilder sb = new StringBuilder(binOutput);
if (binOutput.length() !=7)
{
int DIFF = 7 - binOutput.length(); //As it is an Ascii Value, must be less than length 7
for (int j = 7; j > (7 - DIFF); j--)
{
sb.insert(0,"0");
}
}
binOutput = sb.toString();
hm.put(currentSBox, binOutput);
}
br.close();
return hm;
}
//This method performs bitwise XOR on each block of the plaintext and the bits of the round key (which are the same size)
public static String[] xorSubkey(String[] currentText, String roundKey )
{
for (int i =0; i < roundKey.length(); i++)
{
StringBuilder sb = new StringBuilder();
String binStr = currentText[i];
for (int j =0; j < 7; j++)
{
int chCurrent = Character.getNumericValue(binStr.charAt(j)); //Either 0 or 1
int chKey = Character.getNumericValue(roundKey.charAt((1 * 7)+ j));
if (chCurrent == chKey)
{
sb.append("0");
}
else
{
sb.append("1");
}
}
currentText[i] = sb.toString();
}
return currentText;
}
public static String[] sBoxSubstitution(String[] currentText, HashMap <String,String> hm)
{
for (int i =0; i < currentText.length; i++)
{
String val = hm.get(currentText[i]);
currentText[i] = val;
}
return currentText;
}
public static String[] linTransform(String[] currentText)
{
int shift = currentText.length % 7;
for (int i =0; i < currentText.length; i++)
{
StringBuilder sb = new StringBuilder(currentText[i]);
String sub = currentText[i].substring(shift, 7);
sb.insert(0, sub);
sb.delete(currentText[i].length(), currentText[i].length() + (7 - shift));
currentText[i] = sb.toString();
}
return currentText;
}
public static String[] encrypt(String plaintext, String[] roundKeys) throws IOException
{
String[] binaryText = plaintextToCharacterBinarySequence(plaintext);
HashMap<String, String> hm = defineandStoreSBoxes(f, plaintext.length(), (int) plaintext.charAt(0));
String[] xoredSequence, substitutedSequence,transformedSequence = null;
//The first Num_Rounds -1 Rounds
for (int i =0; i < NUM_ROUNDS -1; i++)
{
xoredSequence = xorSubkey(binaryText, roundKeys[i]);
substitutedSequence = sBoxSubstitution(xoredSequence, hm);
transformedSequence = linTransform(substitutedSequence);
}
// the final round
xoredSequence = xorSubkey(transformedSequence, roundKeys[roundKeys.length - 2]); //Make sure this isnt null
substitutedSequence = sBoxSubstitution(xoredSequence, hm);
//Final xor Subkeying
String[] ciphertext = xorSubkey(substitutedSequence, roundKeys[roundKeys.length - 1]); //Make sure this isnt null
return ciphertext;
}
//DECRYPTING
public static String[] decrypt(String textToDecrypt, String[] roundKeys) throws IOException
{
String[] bitCiphertext = plaintextToCharacterBinarySequence(textToDecrypt);
HashMap<String, String> hm= defineandStoreSBoxes(f,textToDecrypt.length(), (int)textToDecrypt.charAt(0) ); //Make sure this is reversed
String[] xoredSequence, substitutedSequence,transformedSequence = null;
//Decrypting final ciphertext
String[] finalXOR = xorSubkey(bitCiphertext, roundKeys[roundKeys.length - 1]);
// Final round
substitutedSequence = reverseSBox(finalXOR, hm);
xoredSequence = xorSubkey(substitutedSequence, roundKeys[roundKeys.length - 2]);
//Reversing the loop order
for (int i = NUM_ROUNDS -1; i >= 0; i--)
{
transformedSequence = reverseLinTransform(substitutedSequence);
substitutedSequence = reverseSBox(transformedSequence, hm);
xoredSequence = xorSubkey(substitutedSequence, roundKeys[i]);
}
String[] plaintext = xoredSequence;
return plaintext;
}
public static String[] reverseSBox(String[] value, HashMap <String, String> hm)
{
for (int i =0; i < value.length; i++)
{
for (Map.Entry<String, String> entry : hm.entrySet())
{
if (value[i].equals(entry.getValue())) //Because s-boxes are bijective
{
value[i] = entry.getKey();
}
}
}
return value;
}
public static String[] reverseLinTransform(String[] value)
{
int shift = 7 - (value.length %7);
for (int i =0; i < value.length; i++)
{
StringBuilder sb = new StringBuilder(value[i]);
String sub = value[i].substring(shift, 7);
sb.insert(0, sub);
sb.delete(value[i].length(), value[i].length() + (7 - shift));
value[i] = sb.toString();
}
return value;
}
public static boolean isGoodRoundKey(String key, int plainTextLength)
{
if (plainTextLength == key.length() && key.length() % 7 ==0 && isBinary(key) ==true)
{
return true;
}
else
{
return false;
}
}
public static boolean isBinary(String number)
{
for (int i =0; i < number.length(); i++)
{
char c = number.charAt(i);
if (c != '0' && c != '1')
{
return false;
}
}
return true;
}
public static void main(String[] args) throws IOException
{
System.out.println("Inside main");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //Buffered reader will read in plaintext, and round keys
System.out.print("Enter the text that you would like to encrypt: ");
String plaintext = br.readLine();
int numChars = plaintext.length();//The total number of characters that make up this string
String[] roundKeys = new String[NUM_ROUNDS + 1];//Each box will have 7 bits
System.out.println("The cipher will encrypt for "+ NUM_ROUNDS + "rounds, so you will need to enter some round keys. ");
System.out.println("Each round key must be a sequence of zeroes and ones, and must be exactly seven times the length of the plaintext string");
//Storing all the round keys in the array!
for (int i =0; i <= NUM_ROUNDS; i++)
{
if (i ==0)
{
System.out.print("Enter the round key for the 1st round: ");
}
else if (i ==1)
{
System.out.print("Enter the round key for the 2nd round: ");
}
else if (i ==2)
{
System.out.print("Enter the round key for the 3rd round: ");
}
else if (i == NUM_ROUNDS)
{
System.out.print("Enter the round key that will be XORed at the very end of all the rounds ");
}
else
{
System.out.print("Enter the round key for the " + i+1 + "th round: ");
}
roundKeys[i] = br.readLine();
System.out.println();
/*Check to make sure the round key that is passed in is valid. If it is not, an error message will be printed to the user,
prompting them to enter a valid round key. This will loop indefinitely until a valid round key is entered.
*/
if (isGoodRoundKey(roundKeys[i],plaintext.length() ) !=true)
{
boolean isValid = false;
while(isValid == false)
{
System.out.println("ERROR: the key you entered is not a valid key for this cipher.");
System.out.println("Please enter another key: ");
String new_key = br.readLine();
if (isGoodRoundKey(new_key, plaintext.length()) ==true)
{
roundKeys[i] = new_key;
isValid = true;
}
}
}
}
br.close();
String[] bitCiphertext = encrypt(plaintext, roundKeys);
String ciphertext = binarySequenceToString(bitCiphertext);
System.out.println("The encryption of " + plaintext + " is: "+ ciphertext);
String[] discoveredBitPlaintext = decrypt(ciphertext, roundKeys);
String discoveredPlaintext = binarySequenceToString(bitCiphertext);
System.out.println("The encryption of " + ciphertext + " is: " + discoveredPlaintext);//Reverse order of round Keys
}
}
I'm trying to code a cipher project where the objective is for a user to send in a text and shift number. The result would be printed accordingly. For instance, if I sent "Hello World" with a shift value of 1, it should print: "Gfmmp Xrsme". The problem is that I'm having trouble with the shifting because I have an arraylist of char values (the alphabet).
This is what I have so far:
import java.util.ArrayList;
public class CaesarCipher
{
int shift;
String inputText;
ArrayList<String> arr;
ArrayList<String> exchange = new ArrayList<String>();
public CaesarCipher()
{
shift = 0;
inputText = "";
}
public CaesarCipher(int s, String iT)
{
shift = s;
inputText = iT;
}
public void alphabet()
{
arr = new ArrayList<String>();
arr.add("A");
arr.add("B");
arr.add("C");
arr.add("D");
arr.add("E");
arr.add("F");
arr.add("G");
arr.add("H");
arr.add("I");
arr.add("J");
arr.add("K");
arr.add("L");
arr.add("M");
arr.add("N");
arr.add("O");
arr.add("P");
arr.add("Q");
arr.add("R");
arr.add("S");
arr.add("T");
arr.add("U");
arr.add("V");
arr.add("W");
arr.add("X");
arr.add("Y");
arr.add("Z");
}
public void convert()
{
String revisedText = inputText.replaceAll("\\s","");
//Turn revisedText into an array and match it with array above
revisedText.toUpperCase();
int j = 1;
String letter = "";
for (int i = 0; i < revisedText.length(); i++)
{
exchange.add(revisedText.substring(i, j));
j++;
}
}
public void shift()
{
shift = shift % 26 + 26;
ArrayList<String> newArr = new ArrayList<String>(); // array with shifted values
int pos = 0;
for(int r = 0; r < exchange.size(); r++)
{
if(arr.get(r).equals(exchange.get(r)))
arr.indexOf(r) + shift = pos;
}
}
public String toString()
{
return "";
}
}
Here is quick for loop for an example
String string = "Hello World";
String newPhrase = "";
int shift = 1;
for(int i = 0; i < string.length(); i++){
if(string.charAt(i) != ' ')
newPhrase += (char)(string.charAt(i) + shift);
else
newPhrase += ' ';
}
System.out.println(string + " -> " + newPhrase);
Output
Hello World -> Ifmmp Xpsme
However you should note that there are a couple edge cases i am not checking for. Once you understand the above code you should be able to find the edge cases I am talking about.
Example: attack at noon = a3t3c1k1 2n2o2
It also counts spaces.
Here is what I have, but it doesn't seem to be returning correctly:
String getCount(String str) {
String R = "";
int l = S.length();
int cnt = 1;
for (int i = 0; i < l; i++)
for (int j = i + 1; j < l; j++)
if (S.charAt(j) == S.charAt(i)) {
cnt++;
R = R + S.charAt(i)+""+cnt;
System.out.print(S.charAt(i) + cnt);
}
return R;
}
If the strings are small enough you don't need anything fancy, just brute force it (for attack at noon this runs in 3ms).
This code will iterate over all characters, if not found before it will append the character and its count to a StringBuilder which is then printed before terminating.
import java.util.HashMap;
public class Counter {
HashMap<String, Integer> counts;
StringBuilder result;
public static void main(String[] args) {
Counter counter = new Counter();
counter.countString("attack at noon");
}
void countString(String S) {
counts = new HashMap<String, Integer>();
result = new StringBuilder();
String[] split = S.split("");
for (int i = 1; i < split.length; i++) {
String c = split[i];
countChar(c, S);
}
System.out.println(result);
}
void countChar(String c, String s) {
Integer integer = counts.get(c);
if (integer == null) {
int i = s.length() - s.replace(c, "").length();
counts.put(c, i);
result.append(c).append(i);
}
}
}
public static void main(String[] args) {
System.out.println(getCount("attack at noon"));
}
String getCount(String str) {
String R = "";
String S = str;
int l = S.length();
int cnt;
first:for (int i = 0; i < l; i++) { //Foreach letter
cnt = 0;
for (int j = i; j < l; j++) {
if (S.charAt(j) == S.charAt(i)) {
if ( R.indexOf(S.charAt(j)) != -1 ) {
continue first;
}
cnt++;
}
}
R = R + S.charAt(i) + cnt;
}
return R;
}
Output: a3t3c1k1 2n2o2
You should maintain a int[] that stores the counts of each letter in the alphabet.
private int[] getDuplicates(String string) {
String str = string.toLowerCase();
int[] charCounts = new int[27];
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == ' ') {
charCounts[26]++;
}
else {
charCounts[c - 'a']++;
}
}
return charCounts;
}
This method will return an array where the 0th index corresponds to the number of times 'a' shows up in your string
public String getCharCounts(String string) {
int[] charCounts = getDuplicates(string);
StringBuffer strBuff = new StringBuffer();
for (int i = 0; i < charCounts.length; i++) {
if (charCounts[i] > 0) {
char c;
if (i < 26) {
c = (char)(i + (int)'a');
}
else {
c = ' ';
}
strBuff.append(c);
strBuff.append(charCounts[i]);
}
}
return strBuff.toString();
}
This method uses the array you get from the first method and appends the character followed by its count. This is more efficient because you only need to pass through the String one time to get the counts and then pass one time through the array to formulate your deduped String. The run-time is O(n)
Here's my enterprise level solution
attack at noon = a3t3c1k1 2n2o2
Here's another test of the code:
For loop that counts how many characters repeat in a string,
then removes the repeated characters =
f1o6r9 15l1p3t10h6a9c5u1n5s5w1m2y1e11i2g1,1v1d1
And here's the code:
package com.ggl.testing;
import java.util.ArrayList;
import java.util.List;
public class Counter {
private List<CharacterCount> characterCounts;
public Counter() {
this.characterCounts = new ArrayList<CharacterCount>();
}
public void countString(String s) {
s = s.toLowerCase();
for (int i = 0; i < s.length(); i++) {
CharacterCount cc = new CharacterCount(s.charAt(i));
addCharacterCount(cc);
}
}
private void addCharacterCount(CharacterCount cc) {
for (CharacterCount count : characterCounts) {
if (count.getCharacter() == cc.getCharacter()) {
count.setCount(count.getCount() + 1);
return;
}
}
cc.setCount(1);
characterCounts.add(cc);
}
public String returnString() {
StringBuilder builder = new StringBuilder();
for (CharacterCount count : characterCounts) {
builder.append(count.getCharacter());
builder.append(count.getCount());
}
return builder.toString();
}
public static void main(String[] args) {
String s = "For loop that counts how many characters " +
"repeat in a string, then removes the repeated " +
"characters";
Counter counter = new Counter();
counter.countString(s);
System.out.println(s + " = " + counter.returnString());
}
public class CharacterCount {
private char character;
private int count;
public CharacterCount(char character) {
this.character = character;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public char getCharacter() {
return character;
}
}
}
Use a LinkedHashMap to store the counts for each character because it maintains insert order.
public static String countChars(String str) {
Map<Character, Integer> map = new LinkedHashMap<Character, Integer>();
for( char c : str.toCharArray() ) {
if( map.containsKey( c ) ) {
map.put( c, map.get( c ) + 1 );
} else {
map.put( c, 1 );
}
}
StringBuilder sb = new StringBuilder();
for( Character key : map.keySet() ) {
sb.append(key.toString() + map.get( key ) );
}
return sb.toString();
}
Using Guava.
String getGroup(final String string) {
StringBuffer stringBuffer = new StringBuffer();
for(char c : string.toCharArray()) {
int occurances = CharMatcher.is(c).countIn(string);
if (stringBuffer.indexOf(String.valueOf(c)) == -1) {
stringBuffer.append(String.valueOf(c) + occurances);
}
}
return stringBuffer.toString();
}