Compress the string in java - java

Please help with the java code below.
When I give input, for example, aabbcccd,
the output is 99100102d, but it should be a2b2c3d.
Can anyone tell what's my mistake in this code? (This code tries to capture input and output how often a specific char has been typed)
import java.util.*;
public class Main {
public static void main(String args[]) {
try {
Scanner scn = new Scanner(System.in);
String s = scn.nextLine(); // taking input
StringBuilder str = new StringBuilder(s);
StringBuilder str_new = new StringBuilder();
int i = 0 ;
while (i < str.length()) {
int count = 1;
while (i < str.length()-1 && str.charAt(i) == str.charAt(i+1)){
count += 1;
i++;
}
if (count == 1)
str_new.append(str.charAt(i));
else
str_new.append(str.charAt(i) + (char)count);
i++;
}
System.out.println(str_new);
} catch (Exception e) {
return;
}
}
}

The problem comes from str.charAt(i) + (char)count, as they are 2 chars, they are summed up with their int value,
Solve that by using consecutive append() calls
str_new.append(str.charAt(i)).append(count);
You can reduce the code by using an outer for-loop and a ternary operator in the append, and increment only i in the inner while by saving i before
int count;
for (int i = 0; i < str.length(); i++) {
count = i;
while (i < str.length() - 1 && str.charAt(i) == str.charAt(i + 1)) {
i++;
}
str_new.append(str.charAt(i)).append((i - count) == 0 ? "" : (i - count + 1));
}

Your primary issue was the used of the StringBuilder and entering the values which I show in this example. But in this case I am using regular expressions.
(.) is a capture block that matches on any character
\\1* refers to the first capture block followed by 0 or more of the same character.
The following code constructs the Matcher for the entered text and then continues to find subsequent matches. They could be printed out as found or placed in a StringBuilder as I chose to do.
Scanner scn = new Scanner(System.in);
String text = scn.nextLine();
Matcher m = Pattern.compile("(.)\\1*").matcher(text);
StringBuilder sb = new StringBuilder();
while (m.find()) {
String s = m.group();
int count = s.length();
sb.append(s.charAt(0)).append(count > 1 ? count : "");
}
System.out.println(sb.toString());
for aaabbbbcadb Prints
a3b4cadb

Related

Java String sentence - first letter uppercased, second lovercased and so on

Here is a description:
"Write a program that, given an input sentence, alternates the case of every alphabetic character, starting with uppercase. Spaces and non-alphabetical characters should be added to the final output as is, i.e. they should not be taken into account when alternating between upper/lowercase."
Here is what I've tried and does not work (System.out.println in main method should return correct sentence):
public class Main {
public static void main(String[] args) throws IOException {
InputStreamReader reader = new InputStreamReader(System.in, StandardCharsets.UTF_8);
BufferedReader in = new BufferedReader(reader);
String line;
while ((line = in.readLine()) != null) {
System.out.println(changeToUppercaseOrLowercase(countLettersWithSpaces(line), line));
}
}
private static int countLettersWithSpaces(String sentence) {
int count = 0;
for (int i = 0; i < sentence.length(); i ++)
{
char c = Character.toUpperCase(sentence.charAt(i));
if (c >= 'A' && c <= 'Z' || c == ' ' )
count ++;
}
return count;
}
private static String changeToUppercaseOrLowercase(int countLetters, String sentence) {
StringBuilder stringBuilder = new StringBuilder();
for(int i=0; i<countLetters; i++) {
if (!sentence.substring(i,i+1).equals(" ")) {
if ((i % 2) == 0) {
stringBuilder.append(sentence.substring(i,i+1).toUpperCase());
}
else {
stringBuilder.append(sentence.substring(i,i+1).toLowerCase());
}
}
if (sentence.substring(i,i+1).equals(" ")) {
stringBuilder.append(" ");
i++;
}
}
return stringBuilder.toString();
}
}
But tests says that:
Input data:
We are the world
Expected result:
We ArE tHe WoRlD
Result:
We Re He OrLd
How to solve that? Thank you in advance!
You can use Character.isAlphabetic and keep a counter that is incremented each time a letter is encountered.
public static String alternateCase(String str){
int count = 0;
StringBuilder sb = new StringBuilder(str.length());
for(int i = 0; i < str.length(); i++){
char c = str.charAt(i);
if(Character.isAlphabetic(c))
sb.append(++count % 2 == 1 ? Character.toUpperCase(c) : Character.toLowerCase(c));
else sb.append(c);
}
return sb.toString();
}
use Character.isLetter() function to check if it's a letter or not. half your problem will be solved.
and your problem description and test case doesnt go with each other. Please try to clarify more.
There are many ways to fix this. This one has minimal impact on your existing code.
Use an evenOdd counter to ensure you are not skipping over characters but still maintaining the alternation.
private static String changeToUppercaseOrLowercase(int countLetters, String sentence) {
StringBuilder stringBuilder = new StringBuilder();
int evenOdd = 0; // init ********HERE*******
for(int i=0; i<countLetters; i++) {
if (!sentence.substring(i,i+1).equals(" ")) {
if ((evenOdd % 2) == 0) { // check ********HERE*******
stringBuilder.append(sentence.substring(i,i+1).toUpperCase());
}
else {
stringBuilder.append(sentence.substring(i,i+1).toLowerCase());
}
}
if (sentence.substring(i,i+1).equals(" ")) {
stringBuilder.append(" ");
evenOdd--; // adjust to preserve proper alternation ********HERE*********
}
evenOdd++; // the normal update ********HERE*******
}
return stringBuilder.toString();
}

How to compress string on java without using map

I've recently started java and I want to compress a string like this:
Input:aaaaabbbbwwwccc Output:a5b4w3c3
Input:aaabbccds Output:a3b2c2ds
Input:Abcd Output:Abcd
The following code is what I have done but, it does not work.
public class CompressString {
public static void main(String[] args) {
String out = "";
Scanner in = new Scanner(System.in);
String input = in.next();
int length = input.length();
int counter = 1;
if (length == 0) {
System.out.println(" ");
} else {
for (int i = 0; i<length;i++){
if (input.charAt(i)==input.charAt(i+1)){
counter++;
}else {
if (counter == 1){
out = out+input.charAt(i-counter);
}else{
out = out+input.charAt(i-counter)+counter;
}
}
i++;
counter = 1;
}
System.out.println(out.toString());
}
}
}
The simplest program to do that would loop through each character in the string and check when the character is different from the previous seen one and, if so, add the last one and its count to the compressed string:
String input = "aaaaabbbbwwwccc";
StringBuilder compressed = new StringBuilder();
char last = 0;
int lastCount = 0;
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (last == 0 || c != last) {
if (lastCount != 0) {
compressed.append(last);
if (lastCount > 1) {
compressed.append(lastCount);
}
}
last = c;
lastCount = 1;
} else {
lastCount++;
}
}
// take care of the last repeating sequence if any
if (lastCount > 0) {
compressed.append(last);
if (lastCount > 1) {
compressed.append(lastCount);
}
}
Here is a very compact way of doing this with a regex matcher along with a string buffer:
String input = "aaaaabbbbwwwccc";
Pattern r = Pattern.compile("(.)\\1{0,}");
Matcher m = r.matcher(input);
StringBuffer buffer = new StringBuffer();
while (m.find()) {
m.appendReplacement(buffer, m.group(1) + m.group(0).length());
}
m.appendTail(buffer);
System.out.println(buffer.toString());
This prints:
a5b4w3c3
For an explanation, the above logic searches for the regex pattern (.)\1{0,}. This will match any single character, along with that same character occurring again possibly one or more times afterwards. It then replaces with just the single character followed by the count of the number of times it occurs.

implement basic string compression

I am working on question 1.5 from the book Cracking The Coding interview. The problem is to take a string "aabcccccaaa" and turn it into a2b1c5a3.
If the compressed string is not smaller than the original string, then return the original string.
My code is below. I used an ArrayList because I would not know how long the compressed string would be.
My output is [a, 2, b, 1, c, 5], aabc, []. When the program gets to the end of string, it doesn't have a character to compare the last character too.
import java.util.*;
import java.io.*;
public class stringCompression {
public static void main(String[] args) {
String a = "aabcccccaaa";
String b = "aabc";
String v = "aaaa";
check(a);
System.out.println("");
check(b);
System.out.println("");
check(v);
}
public static void check(String g){
ArrayList<Character> c = new ArrayList<Character>();
int count = 1;
int i = 0;
int h = g.length();
for(int j = i + 1; j < g.length(); j++)
{
if(g.charAt(i) == g.charAt(j)){
count++;
}
else {
c.add(g.charAt(i));
c.add((char)( '0' + count));
i = j;
count = 1;
}
}
if(c.size() == g.length()){
System.out.print(g);
}
else{
System.out.print(c);
}
}
}
In the last loop you're not adding the result to the array. When j = g.length() still needs to add the current char and count to the array. So you could check the next value of j before increment it:
for(int j = i + 1; j < g.length(); j++)
{
if(g.charAt(i) == g.charAt(j)){
count++;
}
else {
c.add(g.charAt(i));
c.add((char)( '0' + count));
i = j;
count = 1;
}
if((j + 1) = g.length()){
c.add(g.charAt(i));
c.add((char)( '0' + count));
}
}
I would use a StringBuilder rather than an ArrayList to build your compressed String. When you start compressing, the first character should already be added to the result. The count of the character will be added once you've encountered a different character. When you've reached the end of the String you should just be appending the remaining count to the result for the last letter.
public static void main(String[] args) throws Exception {
String[] data = new String[] {
"aabcccccaaa",
"aabc",
"aaaa"
};
for (String d : data) {
System.out.println(compress(d));
}
}
public static String compress(String str) {
StringBuilder compressed = new StringBuilder();
// Add first character to compressed result
char currentChar = str.charAt(0);
compressed.append(currentChar);
// Always have a count of 1
int count = 1;
for (int i = 1; i < str.length(); i++) {
char nextChar = str.charAt(i);
if (currentChar == nextChar) {
count++;
} else {
// Append the count of the current character
compressed.append(count);
// Set the current character and count
currentChar = nextChar;
count = 1;
// Append the new current character
compressed.append(currentChar);
}
}
// Append the count of the last character
compressed.append(count);
// If the compressed string is not smaller than the original string, then return the original string
return (compressed.length() < str.length() ? compressed.toString() : str);
}
Results:
a2b1c5a3
aabc
a4
You have two errors:
one that Typo just mentioned, because your last character was not added;
and another one, if the original string is shorter like "abc" with only three chars: "a1b1c1" has six chars (the task is "If the compressed string is not smaller than the original string, then return the original string.")
You have to change your if statement, ask for >= instead of ==
if(c.size() >= g.length()){
System.out.print(g);
} else {
System.out.print(c);
}
Use StringBuilder and then iterate on the input string.
private static string CompressString(string inputString)
{
var count = 1;
var compressedSb = new StringBuilder();
for (var i = 0; i < inputString.Length; i++)
{
// Check if we are at the end
if(i == inputString.Length - 1)
{
compressedSb.Append(inputString[i] + count.ToString());
break;
}
if (inputString[i] == inputString[i + 1])
count++;
else
{
compressedSb.Append(inputString[i] + count.ToString());
count = 1;
}
}
var compressedString = compressedSb.ToString();
return compressedString.Length > inputString.Length ? inputString : compressedString;
}

Can anybody help me to correct the following code?

Please help me to identify my mistakes in this code. I am new to Java. Excuse me if I have done any mistake. This is one of codingbat java questions. I am getting Timed Out error message for some inputs like "xxxyakyyyakzzz". For some inputs like "yakpak" and "pakyak" this code is working fine.
Question:
Suppose the string "yak" is unlucky. Given a string, return a version where all the "yak" are removed, but the "a" can be any char. The "yak" strings will not overlap.
public String stringYak(String str) {
String result = "";
int yakIndex = str.indexOf("yak");
if (yakIndex == -1)
return str; //there is no yak
//there is at least one yak
//if there are yaks store their indexes in the arraylist
ArrayList<Integer> yakArray = new ArrayList<Integer>();
int length = str.length();
yakIndex = 0;
while (yakIndex < length - 3) {
yakIndex = str.indexOf("yak", yakIndex);
yakArray.add(yakIndex);
yakIndex += 3;
}//all the yak indexes are stored in the arraylist
//iterate through the arraylist. skip the yaks and get non-yak substrings
for(int i = 0; i < length; i++) {
if (yakArray.contains(i))
i = i + 2;
else
result = result + str.charAt(i);
}
return result;
}
Shouldn't you be looking for any three character sequence starting with a 'y' and ending with a 'k'? Like so?
public static String stringYak(String str) {
char[] chars = (str != null) ? str.toCharArray()
: new char[] {};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
if (chars[i] == 'y' && chars[i + 2] == 'k') { // if we have 'y' and two away is 'k'
// then it's unlucky...
i += 2;
continue; //skip the statement sb.append
} //do not append any pattern like y1k or yak etc
sb.append(chars[i]);
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(stringYak("1yik2yak3yuk4")); // Remove the "unlucky" strings
// The result will be 1234.
}
It looks like your programming assignment. You need to use regular expressions.
Look at http://www.vogella.com/articles/JavaRegularExpressions/article.html#regex for more information.
Remember, that you can not use contains. Your code maybe something like
result = str.removeall("y\wk")
you can try this
public static String stringYak(String str) {
for (int i = 0; i < str.length(); i++) {
if(str.charAt(i)=='y'){
str=str.replace("yak", "");
}
}
return str;
}

Compression algorithm in java

My goal is to write a program that compresses a string, for example:
input: hellooopppppp!
output:he2l3o6p!
Here is the code I have so far, but there are errors.
When I have the input: hellooo
my code outputs: hel2l3o
instead of: he213o
the 2 is being printed in the wrong spot, but I cannot figure out how to fix this.
Also, with an input of: hello
my code outputs: hel2l
instead of: he2lo
It skips the last letter in this case all together, and the 2 is also in the wrong place, an error from my first example.
Any help is much appreciated. Thanks so much!
public class compressionTime
{
public static void main(String [] args)
{
System.out.println ("Enter a string");
//read in user input
String userString = IO.readString();
//store length of string
int length = userString.length();
System.out.println(length);
int count;
String result = "";
for (int i=1; i<=length; i++)
{
char a = userString.charAt(i-1);
count = 1;
if (i-2 >= 0)
{
while (i<=length && userString.charAt(i-1) == userString.charAt(i-2))
{
count++;
i++;
}
System.out.print(count);
}
if (count==1)
result = result.concat(Character.toString(a));
else
result = result.concat(Integer.toString(count).concat(Character.toString(a)));
}
IO.outputStringAnswer(result);
}
}
I would
count from 0 as that is how indexes work in Java. Your code will be simpler.
would compare the current char to the next one. This will avoid printing the first character.
wouldn't compress ll as 2l as it is no smaller. Only sequences of at least 3 will help.
try to detect if a number 3 to 9 has been used and at least print an error.
use the debugger to step through the code to understand what it is doing and why it doesn't do what you think it should.
I am doing it this way. Very simple:
public static void compressString (String string) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < string.length(); i++) {
int count = 1;
while (i + 1 < string.length()
&& string.charAt(i) == string.charAt(i + 1)) {
count++;
i++;
}
if (count > 1) {
stringBuffer.append(count);
}
stringBuffer.append(string.charAt(i));
}
System.out.println("Compressed string: " + stringBuffer);
}
You can accomplish this using a nested for loops and do something simial to:
count = 0;
String results = "";
for(int i=0;i<userString.length();){
char begin = userString.charAt(i);
//System.out.println("begin is: "+begin);
for(int j=i+1; j<userString.length();j++){
char next = userString.charAt(j);
//System.out.println("next is: "+next);
if(begin == next){
count++;
}
else{
System.out.println("Breaking");
break;
}
}
i+= count+1;
if(count>0){
String add = begin + "";
int tempcount = count +1;
results+= tempcount + add;
}
else{
results+= begin;
}
count=0;
}
System.out.println(results);
I tested this output with Hello and the result was He2lo
also tested with hellooopppppp result he2l3o6p
If you don't understand how this works, you should learn regular expressions.
public String rleEncodeString(String in) {
StringBuilder out = new StringBuilder();
Pattern p = Pattern.compile("((\\w)\\2*)");
Matcher m = p.matcher(in);
while(m.find()) {
if(m.group(1).length() > 1) {
out.append(m.group(1).length());
}
out.append(m.group(2));
}
return out.toString();
}
Try something like this:
public static void main(String[] args) {
System.out.println("Enter a string:");
Scanner IO = new Scanner(System.in);
// read in user input
String userString = IO.nextLine() + "-";
int length = userString.length();
int count = 0;
String result = "";
char new_char;
for (int i = 0; i < length; i++) {
new_char = userString.charAt(i);
count++;
if (new_char != userString.charAt(i + 1)) {
if (count != 1) {
result = result.concat(Integer.toString(count + 1));
}
result = result.concat(Character.toString(new_char));
count = 0;
}
if (userString.charAt(i + 1) == '-')
break;
}
System.out.println(result);
}
The problem is that your code checks if the previous letter, not the next, is the same as the current.
Your for loops basically goes through each letter in the string, and if it is the same as the previous letter, it figures out how many of that letter there is and puts that number into the result string. However, for a word like "hello", it will check 'e' and 'l' (and notice that they are preceded by 'h' and 'e', receptively) and think that there is no repeat. It will then get to the next 'l', and then see that it is the same as the previous letter. It will put '2' in the result, but too late, resulting in "hel2l" instead of "he2lo".
To clean up and fix your code, I recommend the following to replace your for loop:
int count = 1;
String result = "";
for(int i=0;i<length;i++) {
if(i < userString.length()-1 && userString.charAt(i) == userString.charAt(i+1))
count++;
else {
if(count == 1)
result += userString.charAt(i);
else {
result = result + count + userString.charAt(i);
count = 1;
}
}
}
Comment if you need me to explain some of the changes. Some are necessary, others optional.
Here is the solution for the problem with better time complexity:
public static void compressString (String string) {
LinkedHashSet<String> charMap = new LinkedHashSet<String>();
HashMap<String, Integer> countMap = new HashMap<String, Integer>();
int count;
String key;
for (int i = 0; i < string.length(); i++) {
key = new String(string.charAt(i) + "");
charMap.add(key);
if(countMap.containsKey(key)) {
count = countMap.get(key);
countMap.put(key, count + 1);
}
else {
countMap.put(key, 1);
}
}
Iterator<String> iterator = charMap.iterator();
String resultStr = "";
while (iterator.hasNext()) {
key = iterator.next();
count = countMap.get(key);
if(count > 1) {
resultStr = resultStr + count + key;
}
else{
resultStr = resultStr + key;
}
}
System.out.println(resultStr);
}

Categories