I am writing a method to decrypt a input string. The encryption is straight forward. Any repeating character in the string is replaced by the character followed by the number of times it appears in the string. So, hello is encrypted as hel2o. Below is the decryption logic I have so far. It works but is so imperative and involves multiple loops. How can this be improved?
String input = "hel2o";
String[] inarr = input.split("\\s+");
StringBuilder sb = new StringBuilder();
for(int i = 0; i < inarr.length; i++) {
String s = inarr[i];
char[] c = s.toCharArray();
for(int j = 0; j < c.length; j++) {
if(Character.isDigit(c[j])) {
for(int x = 0; x < Character.getNumericValue(c[j])-1; x++) {
sb.append(c[j-1]);
}
} else {
sb.append(c[j]);
}
}
}
System.out.printl(sb.toString());
You pretty much asked for a solution but I had fun doing it so I'll share.
You can do it with one loop, by doing some clever appending. Also, unlike yours, my solution will work with multi digit numbers e.g.
Hel23o will convert to helllllllllllllllllllllllo with 23 l's.
String input = "hel23o";
StringBuilder builder = new StringBuilder();
char previousChar = ' ';
StringBuilder number = new StringBuilder();
for (char c : input.toCharArray()) {
if (Character.isDigit(c)) {
number.append(c);
continue;
}
if (number.length() > 0 ) {
int count = Integer.parseInt(number.toString());
count = count > 1 ? count - 1 : 0;
builder.append(String.join("", Collections.nCopies(count, String.valueOf(previousChar))));
}
builder.append(c);
previousChar = c;
number.setLength(0);
}
Alternatively without the multi digit number support:
String input = "hel3o";
StringBuilder builder = new StringBuilder();
char previousChar = ' ';
for (char c : input.toCharArray()) {
if (Character.isDigit(c)) {
builder.append(String.join("", Collections.nCopies(Character.getNumericValue(c) - 1, String.valueOf(previousChar))));
continue;
}
builder.append(c);
previousChar = c;
}
Related
Steps for Deciphering the message
remove 3 at end of the string
replace ASCII values at even places(number clusters) with corresponding characters value.
replace * with spacing " ".
reverse the string
swap case of string- lowercase to upper case and vice versa.
Sample input: ?85O89*69R65*87O104*33I1043
Require output: Hi! How are you?
This is the whole method that I have written.
public String deciphering(String ciphered) {
StringBuilder a = new StringBuilder(ciphered);
StringBuilder b = a.deleteCharAt(a.length()-1);
char[] ch = new char[b.length()];
StringBuilder c = new StringBuilder();
for (int i = 0; i < b.length(); i++) {
ch[i] = b.charAt(i);
}
for (int i = 0; i < ch.length; i++) {
if(!Character.isDigit(ch[i]))
{
c.append(ch[i]);
}else
{
String temp = new String();
while(Character.isDigit(ch[i]) &&(i<b.length())){
temp = temp + ch[i];
i++;
}
int number = Integer.parseInt(temp);
char p = (char)number;
c.append(p);
}
}
String d = c.toString();
String e = d.replace('*', ' ');
StringBuffer f = new StringBuffer(e);
StringBuffer g = f.reverse();
for (int i = 0; i < g.length(); i++) {
if (Character.isLowerCase(g.charAt(i))){
char x = Character.toUpperCase(g.charAt(i));
g.setCharAt(i, x);
} else if (Character.isUpperCase(g.charAt(i))) {
char x = Character.toLowerCase(g.charAt(i));
g.setCharAt(i, x);
}
}
return g.toString();
}
You are incrementing i twice past the last digit of a chunk - when you exit your inner loop i is indexing the first non-digit character, then when you reach the end of the for loop body, the for loop is incrementing i again.
It may be better to split the input string into parts with non-digit delimiters and keep the delimiters using the following regular expression (using look-ahead and look-behind as described here) after removing the last character:
str.substring(0, str.length() - 1).split("(?<=\\D)|(?=\\D)")
Then Stream API may be used to convert each string into a separate character and insert these characters into prepared StringBuilder to implement reverse order in the result:
public static String decipher(String str) {
StringBuilder sb = new StringBuilder(str.length());
Arrays.stream(
str.substring(0, str.length() - 1).split("(?<=\\D)|(?=\\D)")
)
.map(p -> p.matches("\\d+") ? (char) Integer.parseInt(p): p.charAt(0))
.map(c -> c == '*' ? ' '
: Character.isLowerCase(c) ? Character.toUpperCase(c)
: Character.isUpperCase(c) ? Character.toLowerCase(c)
: c
)
.forEach(c -> sb.insert(0, c));
return sb.toString();
}
Test:
System.out.println(decipher("?85O89*69R65*87O104*33I1043"));
Output:
Hi! How are you?
This is part of a java project, we have to take in a sentence that has multiple spaces between the world (a sentence like this) and convert it into a character array, then print out the sentence without all the extra spaces. It works, but it prints out the sentence missing the last letter.
Sentence Class:
public class sentences {
String sentence;
int length;
private char[] ch;
public sentences(String sentence, int length) {
this.sentence = sentence;
this.length = length;
char [] ch;
}
/**method that takes a string and turns it into an array then prints out the array
* makes an empty string and fills it with the char, takes out empty spaces
*
*/
public char[] makesentenceanarray(String sentence, int length) {
char[] ch = new char[sentence.length()];
//String noWhite = "";
StringBuilder s = new StringBuilder();
char prevchar = ' ';
for (int i = 0; i < sentence.length(); i++) {
ch[i] = sentence.charAt(i);
}
for(int j = 0; j < sentence.length(); j++) {
char currentchar = ch[j];
if( !(prevchar == ' ' && currentchar == prevchar)) {
s.append(prevchar);
}
prevchar = currentchar;
}
s.deleteCharAt(0);
System.out.println(ch);
System.out.print(s);
return ch;
}
}
Tester class:
import java.util.Scanner;
public class tester {
public static void main(String [] args) {
Scanner scan = new Scanner(System.in);
System.out.print("enter your sentence: ");
String a = scan.nextLine();
sentences s1 = new sentences(a, a.length());
s1.makesentenceanarray(a, a.length());
}
}
********************************************************************************
heres what I end up with:
enter your sentence: this is my sentence
this is my sentence
this is my sentenc
any help is appreciated thanks!
After the loop, append prevchar. Also, use StringBuilder.
You’re appending the previous character. Think instead about the circumstances under which you would append the current character. In you loop you will look at every current character, but for the very last character, the previous will be the second to last character and then the loop stops. Make sense?
sentence.length() starts counting at 1. (Yes I know it's inconsistent :D)Therefore you need to start counting at 1 and compare with <= at the first for loop.
public char[] makesentenceanarray(String sentence, int length) {
char[] ch = new char[sentence.length()];
//String noWhite = "";
StringBuilder s = new StringBuilder();
char prevchar = ' ';
for (int i = 1; i <= sentence.length(); i++) {
//you should do this now bc of the change of the for loop:
//ch[i - 1] = sentence.charAt(i - 1);
ch[i] = sentence.charAt(i);
}
for(int j = 0; j < sentence.length(); j++) {
char currentchar = ch[j];
if( !(prevchar == ' ' && currentchar == prevchar)) {
s.append(prevchar);
}
prevchar = currentchar;
}
//Now you should be able to delete this
//s.deleteCharAt(0);
System.out.println(ch);
System.out.print(s);
return ch;
}
}
If given a string like "go to med!" how do I replace just the even characters for example? The problem is that while my code takes care of the first word, the space between the words counts as a character itself and messes up replacements in the second word with the first letter of the second word becoming classified as even.
Here is my attempt
public static void main(String[] args) {
String s = "go to med!";
String alphabetS = "abcdefghijklmnopqrstuvwxyz";
StringBuilder sb = new StringBuilder(s);
for(int i=0; i<s.length(); i++){
char currChar = s.charAt(i);
int idx = alphabetS.indexOf(currChar);
if (idx != -1)
if (i%2==1)
{
sb.setCharAt(i, '*');
}
}
System.out.println(sb);
}
This gives output "g+ +o m+d!"
(second letter being correctly replaced by + for being even, but the first letter of the second word should not be replaced as "first" is not "even".
How to make the index to ignore white spaces?
Preferably the answer should not contain arrays, only Char and String methods.
You could simply split the input on the space and process each work individually. You could then use a StringJoiner to piece together the result, for example...
String s = "go to med!!";
String alphabetS = "abcdefghijklmnopqrstuvwxyz";
String[] words = s.split(" ");
StringJoiner sj = new StringJoiner(" ");
for (String word : words) {
StringBuilder sb = new StringBuilder(word);
for (int i = 0; i < sb.length(); i++) {
char currChar = sb.charAt(i);
int idx = alphabetS.indexOf(currChar);
if (idx != -1) {
if (i % 2 == 1) {
sb.setCharAt(i, '*');
}
}
}
sj.add(sb.toString());
}
System.out.println(sj.toString());
which outputs
g* t* m*d!!
could this be done without using arrays - just with char and string methods?
Instead of relying on i, you need a separate counter, which tracks which point your up to and which can be used to ignore invalid characters, for example
String s = "go to med!!";
String alphabetS = "abcdefghijklmnopqrstuvwxyz";
StringBuilder sb = new StringBuilder(s);
int counter = 0;
for (int i = 0; i < sb.length(); i++) {
char currChar = sb.charAt(i);
int idx = alphabetS.indexOf(currChar);
if (idx != -1) {
if (counter % 2 == 1) {
System.out.println("!!");
sb.setCharAt(i, '*');
}
counter++;
}
}
System.out.println(sb.toString());
which still outputs
g* t* m*d!!
this gives: g* t* m*d*
public static void main(String[] args) {
String s = "go to med!";
int realindex=0;
StringBuilder sb = new StringBuilder();
for(int i=0; i<s.length(); i++){
char currChar = s.charAt(i);
if ((currChar != ' '))
{
if (realindex%2==1) {
currChar = '*';
}
realindex++;
}
sb.append(currChar);
}
System.out.println(sb.toString());
}
There are a thousand different ways to accomplish your goal, but assuming you want to keep using your solution, here is what you could do
public static void main(String[] args) {
String s = "go to med!! goodbye cruel world";
String alphabetS = "abcdefghijklmnopqrstuvwxyz";
StringBuilder sb = new StringBuilder(s);
for (int i = 0,relativePosition=0; i < s.length(); i++,relativePosition++) {
char currChar = s.charAt(i);
if(currChar == ' '){relativePosition=-1;continue;}
int idx = alphabetS.indexOf(currChar);
if (idx != -1)
if (relativePosition % 2 == 1)
sb.setCharAt(i, '*');
}
System.out.println(sb);
}
that prints
g* t* m*d!! g*o*b*e c*u*l w*r*d
What about introducing flag about even state of letter?
boolean isEven=false;
for(int i=0; i<s.length(); i++){
char currChar = s.charAt(i);
int idx = alphabetS.indexOf(currChar);
if (idx != -1)
if(isEven){
isEven=false;
sb.setCharAt(i, '*');
}else {
isEven = true;
}
}
}
I wrote the following code but similar characters are always in the same case. What's wrong in this code and How can this problem be solved??
private void genBTActionPerformed(java.awt.event.ActionEvent evt) {
String str = new String(strTF.getText());
int n = str.length();
char ch;
int i;
for(i = 0; i < n; i++) {
if(i % 2 == 0) {
ch = Character.toLowerCase(str.charAt(i));
str = str.replace(str.charAt(i), ch);
} else {
ch = Character.toUpperCase(str.charAt(i));
str = str.replace(str.charAt(i), ch);
}
}
jumTF.setText(str);
}
Unlike what its name says, .replace() replaces characters/CharSequences in the whole input. The difference with .replaceAll() is that it takes literals as arguments and not regexes/regex replacements strings (and that it has an overload taking two chars as arguments). That is the second worst misnamed method of the String class after matches().
Moreover you create a new String on each character you replace, so you have n+1 strings for a n character long string. Do it like this instead:
final char[] chars = str.toCharArray();
final int len = chars.length;
char c;
for (int i = 0; i < len; i++) {
c = chars[i];
chars[i] = i % 2 == 0
? Character.toLowerCase(c)
: Character.toUpperCase(c);
}
jumTF.setText(new String(chars));
In your program you were using replace() which replaces characters/CharSequences in the whole input what you need to do is
Put the string into an array.
Iterate over said array.
convert that array back into string
private void genBTActionPerformed(java.awt.event.ActionEvent evt) {
String str = new String(strTF.getText());
char [] chr= str.toCharArray();
int n = chr.length;
char ch;
int i;
for(i = 0; i < n; i++) {
if(i % 2 == 0) {
ch = Character.toLowerCase(chr[i]);
chr[i]=ch;
} else {
ch = Character.toUpperCase(chr[i]);
chr[i]=ch;
}
}
jumTF.setText(new String(chr)); }
hope this will help you :)
Since String are immutable in java , you can use StringBuilder or StringBuffer to solve this problem
StringBuilder str=new StringBuilder(inputString);
You can use your own logic just with slight change instead of using
str = str.replace(str.charAt(i), ch);//since it replaces in whole string
Use
str.setCharAt(i,ch);
So your final Program looks like this :
for(i = 0; i < n; i++) {
if(i % 2 == 0) {
ch = Character.toLowerCase(str.charAt(i));
str.setCharAt(i,ch);
} else {
ch = Character.toUpperCase(str.charAt(i));
str.setCharAt(i,ch);
}
}
Suppose InputString is : stackoverflow
then output is : sTaCkOvErFlOw
I am attempting to split the components of a string into an array, so that they can be accessed more easily.
For example: 4+5= should become ['4','+','5','='].
Edit: -
I would need consecutive numbers to be joined together, and whitespaces can be ignored. Thanks again!
You can solve it with regex lookaround mechanism.
String str = "10 * 10 - 40 + 100/2 = 110";
//but first lets remove white spaces (it will makes regex easier)
String strWithoutSpaces=str.replaceAll("\\s+", "");
String[] tokens = strWithoutSpaces.split("(?<=[-+*/=])|(?=[-+*/=])");
for (String t:tokens)
System.out.print(t+",");
Output:
10,*,10,-,40,+,100,/,2,=,110,
You can use
String str = "4+5=";
String[] tokens = str.split("(?!^)");
for (String s : tokens) {
System.out.println(s);
}
This will output
4
+
5
=
You could use toCharArray() method
String s ="4+5=";
char [] stArr = s.toCharArray();
for(char ss: stArr){
System.out.println(ss);
}
Out put
4
+
5
=
You could do something like this:
public static void main(String args[]) {
String str = "45+5-26";
String strArr[] = new String[str.length()];
StringBuffer sb = new StringBuffer();
int cnt = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch != '\0') {
if (ch == ' ') {
continue;
}
if (ch >= 48 && ch <= 57) {
sb.append(ch);
continue;
} else {
strArr[cnt++] = sb.toString();
sb = new StringBuffer();
// sb.append(ch);
}
strArr[cnt++] = ch + "";
sb = new StringBuffer();
}
}
strArr[cnt++] = sb.toString();
sb = new StringBuffer();
System.out.println("strArray: ");
for (int i = 0; i < strArr.length; i++) {
if (strArr[i] == null)
break;
System.out.println(strArr[i]);
}
}
If you have only operators as the separator between the numbers this would be more easy to get the string tokens.
You can modify as below if you want the tokens separated by a comma:
for (int i = 0; i < strArr.length; i++) {
if (strArr[i] == null)
break;
// System.out.println(strArr[i]);
if(i!=0)
sbResult.append(",");
sbResult.append(strArr[i]);
}
System.out.println("sbResult: "+sbResult.toString());