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?
Related
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;
}
String evensRemoved = "";
String str = reversedNames[1];
String noSpaces = str.replace(" ","");
int strlength = noSpaces.length();
for(int i = 0; i <= strlength; i++){
if(i % 2 == 0){
StringBuilder sb = new StringBuilder(noSpaces);
sb.deleteCharAt(i);
String result = sb.toString();
return result;
}
}
return "";
I want to be able to remove letters at even positions throughout the string completely, and then return the string to the original method. I've looked at other solutions and haven't been able to figure it out at all. New to Java.
Try this.
It uses a regex that takes two chars at a time and replaces them with the 2nd, thus removing every other one.
the (.) is a capture group of 1 character.
$1 is a back reference to it.
String s = "abcdefghijklmnopqrstuvwxyz";
s = s.replaceAll("(?s).(.)?", "$1");
System.out.println(s);
Prints
bdfhjlnprtvxz
per Andreas suggestion, I preceded the regex with a flag that lets . match returns and linefeeds.
To remove all characters at even indexes, copy all the characters at odd indexes to a new char[].
public static String removeEvens(String str) {
char[] buf = new char[str.length() / 2];
for (int i = 0; i < buf.length; i++)
buf[i] = str.charAt(i * 2 + 1);
return new String(buf);
}
Test
String str = "0123456789";
for (int i = 0; i <= str.length(); i++) {
String sub = str.substring(0, i);
System.out.println('"' + sub + "\" -> \"" + removeEvens(sub) + '"');
}
Output
"" -> ""
"0" -> ""
"01" -> "1"
"012" -> "1"
"0123" -> "13"
"01234" -> "13"
"012345" -> "135"
"0123456" -> "135"
"01234567" -> "1357"
"012345678" -> "1357"
"0123456789" -> "13579"
Example (replacing 'text' with '...'):
Before:
text(text)text
After:
...(text)...
In this case it is easier to find what you want to keep, and replace the rest.
E.g. like this:
static String abbreviate(String input, String openTag, String closeTag) {
String regex = Pattern.quote(openTag) + ".*?" + Pattern.quote(closeTag);
StringBuilder buf = new StringBuilder();
int start = 0;
for (Matcher m = Pattern.compile(regex).matcher(input); m.find(); start = m.end()) {
if (start < m.start())
buf.append("...");
buf.append(m.group());
}
if (start < input.length())
buf.append("...");
return buf.toString();
}
Test
System.out.println(abbreviate("text(text)text(text)text", "(", ")"));
System.out.println(abbreviate("text$text$text$text$text$text", "$", "$"));
System.out.println(abbreviate("text(text)text", ")", "("));
Output
...(text)...(text)...
...$text$...$text$...
...
You need to iterate over the characters and only append those that are between the two specified characters. This can be done as follows:
private String splitStr(String str, char first, char second) {
StringBuilder sb = new StringBuilder();
if(str.isEmpty() || str.indexOf(first) < 0 || str.indexOf(second) < 0)
return sb.toString();
char[] chars = str.toCharArray();
boolean append = false;
for(char c : chars) {
if(c == first) {
sb.append(c);
append = true;
}else if(c == second) {
sb.append(c);
append = false;
}else if(append)
sb.append(c);
}
return sb.toString();
}
Some sample cases are:
"text(text)text(text)text(text)text" => "(text)(text)(text)"
"text(text)text" => "(text)"
String s = "text(text)text";
String newString = s.replace("text", "...");
System.out.println(newString); //returns ...(...)...
Note that "(text)" still contains "text", the braces around it will not stop it from being replaced.
You need to assign the result to a new String to use it. Strings are immutable
I am developing a method which takes ArrayList as an argument.
Then, the method makes some changes into the array and returns transformed arrayList.
The input array is going to be like that {A123, C123, 15B2} and I would like to get the following output {Aa123, Cc123, 15Bb2}.
That is to say, after any capital letter I need to add the same lowercase letter.
And there are any order and quantity of letters, e.g. it is also possible to get strings like those Hkjk124, hy71.
The method is shown below:
protected ArrayList<String> enrichValues(ArrayList<String> list) {
for (int i = 0; i < list.size(); i++) {
char[] charArray = list.get(i).toCharArray();
List<Character> listChars = new ArrayList<>();
for (char c : charArray) {
listChars.add(c);
}
for (int j = 0; j < listChars.size(); j++) {
if (listChars.get(j).charValue() == 'A') {
listChars.add(j + 1, 'a');
}
}
String newChar = "";
for (Character c : listChars)
newChar += c.toString();
list.set(i, newChar);
}
return list;
}
The main problem I have faced to is that I do not know how to check if a letter is uppercase.
I failed to apply something like:
if(Character.isLetter(c) && Character.isUpperCase(c)) {
listChars.add(j + 1, 'a');
}
Because of that I have to add lots of checks:
if (listChars.get(j).charValue() == 'B') {
listChars.add(j + 1, 'b');
}
if (listChars.get(j).charValue() == 'C') {
listChars.add(j + 1, 'c');
}
But it is a very bad approach. I would appreciate any help.
Here's a way of doing it that works like a charm :
public static ArrayList<String> enrichValues(ArrayList<String> values){
ArrayList<String> array = new ArrayList<String>();
for (String str : values){ //For each string
StringBuilder copy = new StringBuilder();
for (char c : str.toCharArray()) {//For each char
copy.append(c);
if(Character.isLetter(c) && Character.isUpperCase(c)){
copy.append(Character.toLowerCase(c));
}
}
array.add(copy.toString());
}
return array;
}
Example :
public static void main(String[] args) {
String a = "A123";
String b = "C123";
String c = "15B2";
String d = " Hkjk124";
String e = "hy71";
String g = "AbCdE645 DeeeFFD";
ArrayList<String> values = new ArrayList<String>();
values.add(a);
values.add(b);
values.add(c);
values.add(d);
values.add(e);
values.add(g);
values = enrichValues(values);
System.out.println(values.toString());
}
Output : [Aa123, Cc123, 15Bb2, Hhkjk124, hy71, AabCcdEe645 DdeeeFfFfDd]
When you are writing some method which accepts concrete implementation of the List interface such as ArrayList, consider to change it to List type. This will allow you to pass in any form of list: LinkedList, ArrayList, ...
Another thing you shoud know is, that joining strings via += is inefficient, as it creates new String instance each time += is applied. Instead of doing this, you should use StringBuilder which allocates resizable buffer for string where you can append other characters.
Condition Character.isLetter(c) && Character.isUpperCase(c) is redudant, since Character.isUpperCase(char) already returns false for non-letter characters.
If you need to convert character to lower-case use Character.toLowerCase(char).
Note, characters are basically integers, so when you write something like this: char c = 65; and print the value, you will see 'A' in output, because 65 is ASCII value for character 'A'. If you add 32, you will obtain 97 which is 'a'. Putting all together you can write something like this:
char c = ...;
// c is in range of upper-case characters
if (c >= 65 && c <= 90) {
char lower = c + 32;
}
// c is in range of lower-case characters
if (c >= 97 && c <= 122) {
char upper = c - 32;
}
Try following method which mutates original list:
protected List<String> enrichValues(List<String> list) {
for (int i = 0; i < list.size(); i++) {
StringBuilder sb = new StringBuilder(list.get(i));
for (int j = 0; j < sb.length(); j++) {
char c = sb.charAt(j);
if ( Character.isUpperCase(c) ) {
sb.insert(++j, Character.toLowerCase(c));
}
}
list.set(i, sb.toString());
}
return list;
}
or this one which creates new list for transformed values:
protected List<String> enrichValues(List<String> original) {
List<String> transformed = new ArrayList<>(list.size());
for (String s : original) {
StringBuilder sb = new StringBuilder(s);
for (int j = 0; j < sb.length(); j++) {
char c = sb.charAt(j);
if ( Character.isUpperCase(c) ) {
sb.insert(++j, Character.toLowerCase(c));
}
}
transformed.add(sb.toString());
}
return transformed;
}
Test:
System.out.println( enrichValues(Arrays.asList("A123", "C123", "15B2")) );
// Output: [Aa123, Cc123, 15Bb2]
To find out whether if a letter is upper case you just need to use the ASCII alphabet. Upper case characters go from 65 to 90.
So you just need to use a loop over the length of your string and check for each char if it´s ASCII value is between 65 and 90. So it would look like this, assuming the variable temp is one character of the string:
if((int)temp >= 65 && (int)temp <= 90){
//Add the lower case character by adding 22 to the character value (lower case are from 97-122)
char additionalChar = ((int)temp)+22;
}
Note that I´ve not tried the code so ((int)temp)+22 might not work that way, but it would look pretty similar.
There is an easier way to do this with regular expressions:
final ArrayList< String > outputList = new ArrayList<>();
for ( String str : list ) {
final String[] letters = str.replaceAll( "[^A-Z]", "" ).toLowerCase().split( "" );
final String result = String.format( str.replaceAll( "([A-Z])", "$1%s" ), letters );
outputList.add( result );
}
return outputList;
I am trying to figure out how to write a method that will remove letters in a
string based on another string. The method would end up like so:
removeLetter("file", "fe")
The only thing that should be returned is the string "il". So far I have something like this:
public class h
{
public static void main(String[] args)
{
String a="file";
String b="fe";
char letter;
int i;
int j;
for (letter = 'a'; letter <= 'z'; letter++)
{
for (i=0; i < a.length()-1; i++)
{
for (j=0; j < b.length()-1; j++) // This is the loop i get stuck on
{
char r = b.charAt(j);
char s = a.charAt(i);
if ( letter == r && letter == s);
System.out.print(r + " " + s);
}
}
}
}
}
I know the bottom part is wrong but I am not sure where to go from here.
You can do this with a regular expression:
a.replaceAll("[" + b + "]", "")
This works by constructing a character class like [fe], and replacing characters which match that with the empty string.
Of course, this is a bit of a hack, in that you can easily choose b such that it won't yield a valid regular expression. However, if you know that b will only ever contain letters, this would work.
Here's a pretty simple nested array using a flag boolean :
public static void main(String[] args) {
String a = "file";
String b = "f";
String c = "";
StringBuilder sb = new StringBuilder();
boolean contains;
for (int i = 0 ; i < a.length() ; i++){
contains = false;
for (int j = 0 ; j < b.length() ; j++){
if (a.charAt(i) == b.charAt(j)) contains = true;
}
if (!contains) sb.append(a.charAt(i));
}
System.out.println(sb);
}
It checks every char of the first word with the chars of the second and changes the flag to true if the char is contained in both.
If it is not the case, the char of the first word is added to the new String, if the contrary, nothing happens and we continue to the next char of the first String.
Let's remove all the vowels of this word : Supercalifragilisticexpialidocious
String a = "Supercalifragilisticexpialidocious";
String b = "aeiou";
Here's the output :
Sprclfrglstcxpldcs