How to compress string on java without using map - java

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.

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();
}

Compress the string in 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

How do I reverse the order of only the digits in a string?

Given a string in Java, how can I obtain a new string where all adjacent sequences of digits are reversed?
My code:
import static java.lang.System.*;
public class P2
{
public static void main(String[] args)
{
if(args.length < 1)
{
err.printf("Usage: java -ea P2 String [...]\n");
exit(1);
}
String[] norm = new String[args.length];
for(int i = 0; i<norm.length;i++)
{
norm[i] = args[i];
}
}
public String invertDigits(String[] norm)
{
}
}
And as an example, this is what it should do:
Inputs: 1234 abc9876cba a123 312asd a12b34c56d
1234 -> 4321
abc9876cba -> abc6789cba
a123 -> a321
312asd -> 213asd
a12b34c56d -> a21b43c65d
Although the question is heavily downvoted, the proposed problem seems clear now. I chose to solve it using a regular expression match in a recursive function.
private static String reverseDigits(String s) {
// the pattern will match a sequence of 1 or more digits
Matcher matcher = Pattern.compile("\\d+").matcher(s);
// fetch the position of the next sequence of digits
if (!matcher.find()) {
return s; // no more digits
}
// keep everything before the number
String pre = s.substring(0, matcher.start());
// take the number and reverse it
String number = matcher.group();
number = new StringBuilder(number).reverse().toString();
// continue with the rest of the string, then concat!
return pre + number + reverseDigits(s.substring(matcher.end()));
}
And here's the iterative approach.
private static String reverseDigits(String s) {
//if (s.isEmpty()) return s;
String res = "";
int base = 0;
Matcher matcher = Pattern.compile("\\d+").matcher(s);
while (!matcher.hitEnd()) {
if (!matcher.find()) {
return res + s.substring(base);
}
String pre = s.substring(base, matcher.start());
base = matcher.end();
String number = matcher.group();
number = new StringBuilder(number).reverse().toString();
res += pre + number;
}
return res;
}
String str = "1234";
//indexes
int i = 0, j = str.length()-1;
// find digits (if any)
while (!Character.isDigit(str.charAt(i)) && i < str.length()) {
i++;
}
while (!Character.isDigit(str.charAt(j)) && j >= 0) {
j--;
}
// while we havent searched all the digits
while (i < j) {
// switch digits
str = str.substring(0, i) + str.charAt(j) + str.substring(i + 1, j) + str.charAt(i) + str.substring(j + 1);
i++;
j--;
// find the next digits
while (!Character.isDigit(str.charAt(i)) && i < str.length()) {
i++;
}
while (!Character.isDigit(str.charAt(j)) && j >= 0) {
j--;
}
}
System.out.println(str);
Another dynamic approach without using regex classes:
public static String reverseOnlyNumbers(String s) {
StringBuilder digits = new StringBuilder();
StringBuilder result = new StringBuilder();
boolean start = false;
for (int i = 0; i < s.length(); i++) {
Character c = s.charAt(i);
if (Character.isDigit(c)) {
start = true;
digits.append(c);
}else {
start = false;
if (digits.length() > 0) {
result.append(digits.reverse().toString());
digits = new StringBuilder();
}
result.append(c);
}
}
return start ? result.append(digits.reverse()).toString() : result.toString();
}

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);
}

how can i calculate the number of specific chars in a string?

Given a string how can i figure out the number of times each char in a string repeats itself
ex: aaaabbaaDD
output: 4a2b2a2D
public static void Calc() {
Input();
int count = 1;
String compressed = "";
for (int i = 0; i < input.length(); i++) {
if (lastChar == input.charAt(i)) {
count++;
compressed += Integer.toString(count) + input.charAt(i);
}
else {
lastChar = input.charAt(i);
count = 1;
}
}
System.out.println(compressed);
}
What you'r looking for is "Run-length encoding". Here is the working code to do that;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RunLengthEncoding {
public static String encode(String source) {
StringBuffer dest = new StringBuffer();
// iterate through input string
// Iterate the string N no.of.times where N is size of the string to find run length for each character
for (int i = 0; i < source.length(); i++) {
// By default run Length for all character is one
int runLength = 1;
// Loop condition will break when it finds next character is different from previous character.
while (i+1 < source.length() && source.charAt(i) == source.charAt(i+1)) {
runLength++;
i++;
}
dest.append(runLength);
dest.append(source.charAt(i));
}
return dest.toString();
}
public static String decode(String source) {
StringBuffer dest = new StringBuffer();
Pattern pattern = Pattern.compile("[0-9]+|[a-zA-Z]");
Matcher matcher = pattern.matcher(source);
while (matcher.find()) {
int number = Integer.parseInt(matcher.group());
matcher.find();
while (number-- != 0) {
dest.append(matcher.group());
}
}
return dest.toString();
}
public static void main(String[] args) {
String example = "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW";
System.out.println(encode(example));
System.out.println(decode("1W1B1W1B1W1B1W1B1W1B1W1B1W1B"));
}
}
This program first finds the unique characters or numbers in a string. It will then check the frequency of occurance.
This program considers capital and small case as different characters. You can modify it if required by using ignorecase method.
import java.io.*;
public class RunLength {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws IOException {
System.out.println("Please enter the string");
String str = br.readLine();//the input string is in str
calculateFrequency(str);
}
private static void calculateFrequency(String str) {
int length = str.length();
String characters[] = new String[length];//to store all unique characters in string
int frequency[] = new int[length];//to store the frequency of the characters
for (int i = 0; i < length; i++) {
characters[i] = null;
frequency[i] = 0;
}
//To get unique characters
char temp;
String temporary;
int uniqueCount = 0;
for (int i = 0; i < length; i++) {
int flag = 0;
temp = str.charAt(i);
temporary = "" + temp;
for (int j = 0; j < length; j++) {
if (characters[j] != null && characters[j].equals(temporary)) {
flag = 1;
break;
}
}
if (flag == 0) {
characters[uniqueCount] = temporary;
uniqueCount++;
}
}
// To get the frequency of the characters
for(int i=0;i<length;i++){
temp=str.charAt(i);
temporary = ""+temp;
for(int j=0;i<characters.length;j++){
if(characters[j].equals(temporary)){
frequency[j]++;
break;
}
}
}
// To display the output
for (int i = 0; i < length; i++) {
if (characters[i] != null) {
System.out.println(characters[i]+" "+frequency[i]);
}
}
}}
Some hints: In your code sample you also need to reset count to 0 when the run ends (when you update lastChar). And you need to output the final run (after the loop is done). And you need some kind of else or continue between the two cases.
#Balarmurugan k's solution is better - but just by improving upon your code I came up with this -
String input = "aaaabbaaDD";
int count = 0;
char lastChar = 0;
int inputSize = input.length();
String output = "";
for (int i = 0; i < inputSize; i++) {
if (i == 0) {
lastChar = input.charAt(i);
count++;
} else {
if (lastChar == input.charAt(i)) {
count++;
} else {
output = output + count + "" + lastChar;
count = 1;
lastChar = input.charAt(i);
}
}
}
output = output + count + "" + lastChar;
System.out.println(output);

Categories