Fixing a looping issue for removing letters in a String - java

So i'm making a program that removes duplicate letters in a string. The last step of it is updating the old string to the new string, and looping through the new string. I believe everything works besides the looping through the new string part. Any ideas what might be causing it to not work? It will work as intended for one pass through, and then after that it won't step through the new loop
public class homework20_5 {
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
int i;
for (i = 0; i < kb.length(); i++) {
char temp = kb.charAt(i);
if(temp == kb.charAt(i+1)) {
kb = kb.replace(""+temp, "");
i = kb.length() + i;
}
}
System.out.println(kb);
}
}

Instead of using complex algorithms and loops like this you can just use HashSet which will work just like a list but it won't allow any duplicate elements.
private static String removeDuplicateWords(String str) {
HashSet<Character> xChars = new LinkedHashSet<>();
for(char c: str.toCharArray()) {
xChars.add(c);
}
StringBuilder sb = new StringBuilder();
for (char c: xChars) {
sb.append(c);
}
return sb.toString();
}

So you actually want to remove all occurrences that appear more than once entirely and not just the duplicate appearances (while preserving one instance)?
"Yea that’s exactly right "
In that case your idea won't cut it because your duplicate letter detection can only detect continuous sequences of duplicates. A very simple way would be to use 2 sets in order to identify unique letters in one pass.
public class RemoveLettersSeenMultipleTimes {
public static void main(String []args){
String input = "abcabdgag";
Set<Character> lettersSeenOnce = lettersSeenOnceIn(input);
StringBuilder output = new StringBuilder();
for (Character c : lettersSeenOnce) {
output.append(c);
}
System.out.println(output);
}
private static Set<Character> lettersSeenOnceIn(String input) {
Set<Character> seenOnce = new LinkedHashSet<>();
Set<Character> seenMany = new HashSet<>();
for (Character c : input.toCharArray()) {
if (seenOnce.contains(c)) {
seenMany.add(c);
seenOnce.remove(c);
continue;
}
if (!seenMany.contains(c)) {
seenOnce.add(c);
}
}
return seenOnce;
}
}

There are a few problems here:
Problem 1
for (i = 0; i < kb.length(); i++) {
should be
for (i = 0; i < kb.length() - 1; i++) {
Because this
if (temp == kb.charAt(i+1))
will explode with an ArrayIndexOutOfBoundsException otherwise.
Problem 2
Delete this line:
i = kb.length() + i;
I don't understand what the intention is there, but nevertheless it must be deleted.
Problem 3
Rather than lots of code, there's a one-line solution:
String deduped = kb.replaceAll("[" + input.replaceAll("(.)(?=.*\\1)|.", "$1") + "]", "");
This works by:
finding all dupe chars via input.replaceAll("(.)(?=.*\\1)|.", "$1"), which in turn works by consuming every character, either capturing it as group 1 if it has a dupe or just consuming it if a non-dupe
building a regex character class from the dupes, which is used to delete them all (replace with a blank)

Say you feed the program with the input "AAABBC", then the expected output should be "ABC".
Now in the for-loop, i gets incremented from 0 to 5.
After 1st iteration:
kb becomes AABBC and i becomes 5 + 0 = 5 and gets incremented to 6.
And now the condition for the for-loop is that i < kb.length() which equates to 6 < 5 returning false. Hence the for-loop ends after just one iteration.
So the problematic line of code is i = kb.length() + i; and also the loop condition keeps changing as the size of kb changes.
I would suggest using a while loop like the following example if you don't worry too much about the efficiency.
public static void main(String[] arg) {
String kb = "XYYYXAC";
int i = 0;
while (i < kb.length()) {
char temp = kb.charAt(i);
for (int j = i + 1; j < kb.length(); j++) {
char dup = kb.charAt(j);
if (temp == dup) {
kb = removeCharByIndex(kb, j);
j--;
}
}
i++;
}
System.out.println(kb);
}
private static String removeCharByIndex(String str, int index) {
return new StringBuilder(str).deleteCharAt(index).toString();
}
Output: XYAC
EDIT: I misunderstood your requirements. So looking at the above comments, you want all the duplicates and the target character removed. So the above code can be changed like this.
public static void main(String[] arg) {
String kb = "XYYYXAC";
int i = 0;
while (i < kb.length()) {
char temp = kb.charAt(i);
boolean hasDup = false;
for (int j = i + 1; j < kb.length(); j++) {
if (temp == kb.charAt(j)) {
hasDup = true;
kb = removeCharByIndex(kb, j);
j--;
}
}
if (hasDup) {
kb = removeCharByIndex(kb, i);
i--;
}
i++;
}
System.out.println(kb);
}
private static String removeCharByIndex(String str, int index) {
return new StringBuilder(str).deleteCharAt(index).toString();
}
Output: AC
Although, this is not the best and definitely not an efficient solution to this, I think you can get the idea of iterating the input string character by character and removing it if it has duplicates.

The following answer concerns only the transformation of XYYYXACX to ACX. If we wanted to have AC, it's a whole different answer. The other answers already speak about it, and I'll invite you to consult the contains method of String too.
We should consider avoiding -most of the time- modifying the things we iterate. Using a temporary variable could be a kind of solution. To use it, we could change our mindset. Instead of erasing the undesired letters, we can save the ones we want.
To identify the desired character, we need to test if all surrounding letters are different from the tested one. It'll be the opposite of what you did with if(temp == kb.charAt(i+1)) { like if(temp != kb.charAt(i+1)) {. But considering that the tested string will not change anymore, we will need to test the previous letter too as if(temp != kb.charAt(i-1) && temp != kb.charAt(i+1)) {.
As previously said, once we have identified the letter, we will keep the value with a temporary variable. That will lead to replace kb = kb.replace(""+temp, ""); by buffer = buffer + temp; if buffer is our temporary variable initialized with an empty string (Aka. String buffer = "";). In the end, we could override our base value with the temporary one.
At this step, we will have:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
String buffer = "";
int i;
for (i = 1; i < kb.length(); i++) {
char temp = kb.charAt(i);
if(temp != kb.charAt(i-1) && temp != kb.charAt(i+1)) {
buffer = buffer + temp;
}
}
kb = buffer;
System.out.println(kb);
}
That'll sadly not work, trying to access invalid indexes of our string. We should consider two particular behavior for the first and the last letter because they are close to only one letter. For these letters, we will have only one comparison. So, we can make them inside or outside the loop. For clarity, we will do it outside.
For the first one, it will look like to if (kb.charAt(0) != kb.charAt(1)) { and at if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) { for the last. The body of the condition will remain the same as the one in the loop.
Once done, we will reduce the scope of our loop to exclude these character with for (i = 1; i < (kb.length() - 1); i++) {.
Now we will have something working, but only for one iteration:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
String buffer = "";
int i;
if (kb.charAt(0) != kb.charAt(1)) {
buffer = buffer + kb.charAt(0);
}
for (i = 1; i < (kb.length() - 1); i++) {
char temp = kb.charAt(i);
if(temp != kb.charAt(i-1) && temp != kb.charAt(i+1)) {
buffer = buffer + temp;
}
}
if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {
buffer = buffer + kb.charAt(kb.length() - 1);
}
kb = buffer;
System.out.println(kb);
}
XYYYXACX will become XXACX.
Once said, our index problem can occur again if the string has only one letter. However, all of this would have been useless because obviously, we can't have a duplicate letter in this situation. As a fact, we should wrap the whole thing to ensure that we have at least two letters:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
if (kb.length() >= 2) {
String buffer = "";
int i;
if (kb.charAt(0) != kb.charAt(1)) {
buffer = buffer + kb.charAt(0);
}
for (i = 1; i < (kb.length() - 1); i++) {
char temp = kb.charAt(i);
if (temp != kb.charAt(i - 1) && temp != kb.charAt(i + 1)) {
buffer = buffer + temp;
}
}
if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {
buffer = buffer + kb.charAt(kb.length() - 1);
}
kb = buffer;
}
System.out.println(kb);
}
The last thing to do is perform this treatment until we have no more undesired letters. For this task, the do { ... } while ( ... ) seems perfect. We can use for the condition comparison the size of the string. Because when the size of the previous iteration is equal to the temporary variable, we will know that we have finished.
We will need to perform this comparison before affecting the value of our temporary variable to the base one. Otherwise, it'll always be the same.
In the end, the following thing should be a potential solution:
public static void main(String[] arg) {
Scanner scanner = new Scanner(System.in);
String kb = scanner.nextLine();
Boolean modified;
do {
modified = false;
if (kb.length() >= 2) {
String buffer = "";
int i;
if (kb.charAt(0) != kb.charAt(1)) {
buffer = buffer + kb.charAt(0);
}
for (i = 1; i < (kb.length() - 1); i++) {
char temp = kb.charAt(i);
if (temp != kb.charAt(i - 1) && temp != kb.charAt(i + 1)) {
buffer = buffer + temp;
}
}
if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {
buffer = buffer + kb.charAt(kb.length() - 1);
}
modified = (kb.length() != buffer.length());
kb = buffer;
}
} while (modified);
System.out.println(kb);
}
Take note that this code is ugly for the sole purpose of the explanation. We should refactor this code. We can improve it a lot for the sake of brevity and, why not, performance.

Related

How do I remove the last comma from the output in series using loop [duplicate]

So I made this to print primes between two numbers of my choice; however, it prints out a comma after the last number and I don't know how to take it off.
Example
in: 0 10
out: 2, 3, 5, 7,
I want 2,3,5,7
Scanner s = new Scanner(System.in);
int a = s.nextInt();
int b = s.nextInt();
for (int i = a; i <= b; i++){
int j;
for (j = 2; j<i; j++){
int p = i%j;
if(p==0){break;}
}
if(i == j){System.out.printf("%d,", i);}
}
}
Use a boolean to keep track of whether you've printed anything yet. Then your format string could be something like
anythingPrinted ? ",%d" : "%d"
That is, only include the comma in the format string if there's something printed.
Use a StringBuilder and write to the console at the end of your program.
StringBuilder sb = new StringBuilder();
for (int i = a; i <= b; i++){
int j;
for (j = 2; j<i; j++){
int p = i%j;
if(p==0){break;}
}
if(i == j){
// If the length of the StringBuilder is 0, no need for a comma
if(sb.length() != 0) {
sb.append(",");
}
sb.append(i);
}
}
System.out.println(sb);
This might seem like overkill, and for many cases it might be, but I have been writing a source code transcoder and I find this situation coming up a lot. Where I need commas in between values, or a prefix value which is only printed once. So I found it handy to create a class which simplifies things.
Again, you wouldn't probably want to use this if you code had one or two print loops in it, but maybe if you had more than a few. Perhaps you would remove in "on first" part if you were never going to use it.
public class FirstPrintOptions {
private PrintStream printStream;
private String onFirst;
private String remaining;
private boolean trip = false;
public FirstPrintOptions(PrintStream printStream, String onFirst, String remaining) {
this.printStream = printStream;
this.onFirst = onFirst;
this.remaining = remaining;
}
public void print() {
if (!trip) {
if (onFirst != null) {
printStream.print(onFirst);
}
trip = true;
} else {
if (remaining != null) {
printStream.print(remaining);
}
}
}
}
Then use it like this..
FirstPrintOptions firstPrintOptions = new FirstPrintOptions(System.out, null, ",");
for (int x=0;x<10;x++) {
firstPrintOptions.print();
System.out.print(x);
}
The results are..
0,1,2,3,4,5,6,7,8,9
I was testing and I came up with this. I was using compilejava.net so scanner doesn't work. I bypassed that part and just set a and b manually. Basically, it builds a string with the numbers and ends in a comma. Then it prints a substring including everything except the last comma.
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
//Scanner s = new Scanner(System.in);
int a = 2;
int b = 18;
String c = "Output = ";
for (int i = a; i <= b; i++){
int j;
for (j = 2; j<i; j++){
int p = i%j;
if(p==0){break;}
}
if(i == j){c=c+ Integer.toString(i) + ",";}
}
System.out.print(c.subSequence(0, c.length()-1));
}
}
this program for finding factors of a number
for(i=1;i<=number;i++)
{
if(number%i==0)
{
system.out.print(i);
if(i!=0)
{system.out.print(",");}
}
}
so i get the output for 10 as
1,2,5,10

Is there a way to reverse a string faster than O(n)?

I have the following code which takes more than 5 seconds to run with the argument -Xmx<1024M>.
I am aware that the for loop takes O(q) time, as well as the reverse() and toString() take O(n) time each.
Is there a way to reverse the string in less than O(n) time? Or is something else slowing the code down? Any help would be welcome!
class Main {
public static void main(String[] args){
String s = "a";
String qa = "200000";
int q = Integer.parseInt(qa);
String[] t = new String[q];
for(int i = 0; i < q; i++) {
if(i%2==0) {t[i] = "2 1 x";}
if(i%2==1) {t[i] = "1";}
if(t[i].toCharArray()[0] == '1') {
StringBuilder rev = new StringBuilder(s).reverse();
s = rev.toString();
} else {
char letter = t[i].toCharArray()[4];
if(t[i].toCharArray()[2] == '1') {
s = letter + s;
} else {
s = s + letter;
}
}
}
System.out.println(s);
}
}
Regardless of what is it supposed to do (I have no idea), I found the following problems:
Multiple instantinations of StringBuilder in each iteration.
String concatenation using + operator.
Repetitive usage of Sring::toCharArray (see the 2nd solution)
You will achieve a faster result using directly only one instance of StringBuilder:
String s = "a";
String qa = "200000";
int q = Integer.parseInt(qa);
String[] t = new String[q];
StringBuilder sb = new StringBuilder(s); // Instantiate before the loop
for (int i = 0; i < q; i++) {
if(i%2==0) {t[i] = "2 1 x";}
if(i%2==1) {t[i] = "1";}
if(t[i].toCharArray()[0] == '1') {
sb.reverse(); // all you did here is just reversing 's'
} else {
char letter = t[i].toCharArray()[4];
if(t[i].toCharArray()[2] == '1') {
sb.insert(0, letter); // prepend a letter
} else {
sb.append(letter); // append a letter
}
}
}
Another thing is that you multiple times define a String such as t[i] = "2 1 x"; and then you compare with t[i].toCharArray()[0]. Pre-definig these immutable values and using char[][] should help too:
String s = "a";
String qa = "200000";
int q = Integer.parseInt(qa);
char[][] t = new char[q][]; // char[][] instead of String[]
char[] char21x = new char[]{'2', '1', 'x'}; // predefined array
char[] char1 = new char[]{'1'}; // another predefined array
StringBuilder sb = new StringBuilder(s); // Instantiate before the loop
for (int i = 0; i < q; i++) {
if(i%2==0) {t[i] = char21x;} // first reuse
if(i%2==1) {t[i] = char1;} // second reuse
if(t[i][0] == '1') { // instead of String::toCharArray, mind the indices
sb.reverse(); // all you did here is just reversing 's'
} else {
char letter = t[i][2]; // instead of String::toCharArray, mind the indices
if(t[i][1] == '1') {
sb.insert(0, letter); // prepend a letter
} else {
sb.append(letter); // append a letter
}
}
}
Edit: I have tested the solution with the simplest way possible using a difference of System.currentTimeMillis() on my laptop:
Original solution: 7.658, 6.899 and 7.046 seconds
2nd solution: 3.288, 3.691 and 3.158 seconds
3rd solution: 2.717, 2.966 and 2.717 seconds
Conclusion: I see no way to improve the algorithm itself in terms of the computation complexity, however, using the correct ways to treat Strings helps to reduce the time complexity 2-3 times (in my case).
General advice: What you can instantiate and define before the loop, do it before the loop.
Is there a way to reverse the string in less than O(n) time? Or is something else slowing the code down?
No there is no way to reverse a string in less than O(n) time: A program that produces an output of size n necessarily takes o(n) time at the minimum.
Your code has lots of unnecessary operations that slow the program down. The program produces 50000 letters x, followed by one letter a, followed by another 50000 letters x. Here is a much faster (and easier to understand) implementation of the same program.
class Faster {
public static void main(String[] args) {
String hundredXs = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
for (int i = 0; i < 500; i++)
System.out.print(hundredXs);
System.out.print("a");
for (int i = 0; i < 500; i++)
System.out.print(hundredXs);
System.out.println();
}
}

Splitting this string to get the max count to a corresponding character

I am currently implementing Run Length Encoding for text compression and my algorithm does return Strings of the following form:
Let's say we have a string as input
"AAAAABBBBCCCCCCCC"
then my algorithm returns
"1A2A3A4A5A1B2B3B4B1C2C3C4C5C6C7C8C"
Now I want to apply Java String split to solve this, because I want to get the highest number corresponding to character. For our example it would be
"5A4B8C"
My function can be seen below
public String getStrfinal(){
String result = "";
int counter = 1;
StringBuilder sb = new StringBuilder();
sb.append("");
for (int i=0;i<str.length()-1;i++) {
char c = str.charAt(i);
if (str.charAt(i)==str.charAt(i+1)) {
counter++;
sb.append(counter);
sb.append(c);
}
else {
counter = 1;
continue;
}
}
result = sb.toString();
return result;
}
public static String getStrfinal(){
StringBuilder sb = new StringBuilder();
char last = 0;
int count = 0;
for(int i = 0; i < str.length(); i++) {
if(i > 0 && last != str.charAt(i)) {
sb.append(count + "" + last);
last = 0;
count = 1;
}
else {
count++;
}
last = str.charAt(i);
}
sb.append(count + "" + last);
return sb.toString();
}
Here is one possible solution. It starts with the raw string and simply iterates thru the string.
public static void main(String[] args) {
String input = "AAAABBBCCCCCCCDDDEAAFBBCD";
int index = 0;
StringBuilder sb = new StringBuilder();
while (index < input.length()) {
int count = 0;
char c = input.charAt(index);
for (; index < input.length(); index++) {
if (c != input.charAt(index)) {
count++;
}
else {
break;
}
}
sb.append(Integer.toString(count));
sb.append(c);
count = 0;
}
System.out.println(sb.toString());
}
But one problem with this method and others is what happens if there are digits in the text? For example. What if the string is AAABB999222AAA which would compress to 3A2B39323A. That could also mean AAABB followed by 39 3's and 23 A's
Instead of string Buffer you can use a map it will be much easier and clean to do so.
public static void main(String[] args) {
String input = "AAAAABBBBCCCCCCCCAAABBBDDCCCC";
int counter=1;
for(int i=1; i<input.length(); i++) {
if(input.charAt(i-1)==input.charAt(i)) {
counter=counter+1;
}else if(input.charAt(i-1)!=input.charAt(i)){
System.out.print(counter+Character.toString(input.charAt(i-1)));
counter=1;
}if(i==input.length()-1){
System.out.print(counter+Character.toString(input.charAt(i)));
}
}
}
This will gives
5A4B8C3A3B2D4C
UPDATES
I Agree with #WJS if the string contains number the out put becomes messy
hence if the System.out in above code will be exchange with below i.e.
System.out.print(Character.toString(input.charAt(i-1))+"="+counter+" ");
then for input like
AAAAABBBBCCCCCCCCAAABBBDD556677CCCCz
we get out put as below
A=5 B=4 C=8 A=3 B=3 D=2 5=2 6=2 7=2 C=4 z=1
This is one of the possible solutions to your question. We can use a LinkedHashMap data structure which is similar to HashMap but it also maintains the order. So, we can traverse the string and store the occurrence of each character as Key-value pair into the map and retrieve easily with its maximum occurrence.
public String getStrFinal(String str){
if(str==null || str.length()==0) return str;
LinkedHashMap<Character,Integer> map = new LinkedHashMap<>();
StringBuilder sb=new StringBuilder(); // to store the final string
for(char ch:str.toCharArray()){
map.put(ch,map.getOrDefault(ch,0)+1); // put the count for each character
}
for(Map.Entry<Character,Integer> entry:map.entrySet()){ // iterate the map again and append each character's occurence into stringbuilder
sb.append(entry.getValue());
sb.append(entry.getKey());
}
System.out.println("String = " + sb.toString()); // here you go, we got the final string
return sb.toString();
}

String Compression loop logic

My for loop for my string compression is a bit off. I have been working on this assignment the past 5 days and I can't figure out for the life of me what is wrong. Can someone help me out?
For example, I passed over the string "TTTTrrrEe" and instead of getting T4r3Ee, I'm getting T4r3EeTT. I don't know why it jumps back to the beginning of the string like that, but I am getting closer.We can only use charAt,equals,length, and substring from the string class.
Can someone help guide me in the right direction by helping to correct my logic? I still want to try and code this myself, seeing as how it is an assignment.
public static String compress(String s){
int count = 0;
String temp = s.substring(0,1);
for(int i = 0; i < s.length(); i++){
if(i !=s.length()-1){
if(temp.equals(s.substring(i,i+1))){
count++;
}else{
if(count < 1){
System.out.print(s.substring(i,i+2));
System.out.print(temp.substring(0,1) );
}else{
System.out.print("" + temp.substring(0,1) + count);
i--;
temp = s.substring(count,count+1);
System.out.println(" temp is now " + temp);
count = 0;
//i--;
}
}
}
}
System.out.println(temp);
return temp;
}
Since this is a learning exercise, I wouldn't try fixing your code, just point out a few things to work on to get it right:
The if (i !=s.length()-1) condition inside the loop becomes unnecessary if you change your for loop condition to i < s.length()-1
Comparing individual characters is easier (and faster) than comparing substrings. You get a character at position i by calling char ch1 = s.charAt(i), and compare two characters using == operator, rather than calling equals() on them.
When count is zero (your count < 1 condition is equivalent to count == 0) you print both the current character and the character after it, in addition to the first character of temp followed by the count. This does not look correct.
Rather than growing temp as you go through the loop, you set it on each iteration. This does not look correct.
A better way of growing temp as you go through the loop is using StringBuilder and append(), instead of using a plain String, and performing concatenations.
Try using some logic like this;
int count = 0;
for(int i =0; i < string.length()-1; i++){
if(string.charAt(i) == string.charAt(i + 1)){
count++;
// DO SOME OPERATION
}
}
temp = s.substring(count,count+1); does not relate to a position (i), but a size.
In fact I would try to rewrite it afresh, with externally sensible names:
char repeatedChar = `\u0000`; // Not present.
int repetitions = 0;
Because of the no-namer count you got into trouble.
Working code:
public class HelloWorld {
public static void compress(String s){
StringBuilder buff = new StringBuilder();
char tmp = '\0';
int index = 1;
for(int i = 0; i < s.length(); i++){
char curr = s.charAt(i);
if(buff.length() == 0){
tmp = curr;
buff.append(tmp);
continue;
}
if(curr == tmp){
index++;
}
else{
if(index > 1){
buff.append(index);
index = 1;
tmp = curr;
}
buff.append(curr);
}
}
System.out.println(buff.toString());
}
public static void main(String args[]){
compress("TTTTrrrEe");
}
}
Output: T4r3Ee
For compress("TTsssssssssssTTrrrEe");
Output: T2s11T2r3Ee
String temp = s.substring(0,1);
temp.equals(s.substring(i,i+1))
In case of these 2 sentences you should have used a char instead of String, as such:
char temp = s.charAt(0)
temp == s.charAt(i)
I would start with 3 variables:
char lastCharacter = inputString.charAt(0);
int count = 1;
String result = "";
then proceed to process the input string in a loop:
if (length <= 1) return inputString;
for i = 1 ; i < length;i++
if (inputString.charAt(i) == lastCharacter && i != length-1)
count++
else
if count == 1
result += lastCharacter
else
result = result + lastCharacter + count;
count = 1;
end if
lastCharacter = inputString.charAt(i);
end if
end for
return result;
TRY THIS
public class Compress {
/**
* #param args
* #author Rakesh KR
*/
public static String encode(String source) {
StringBuffer dest = new StringBuffer();
for (int i = 0; i < source.length(); i++) {
int runLength = 1;
while (i+1 < source.length() && source.charAt(i) == source.charAt(i+1)) {
runLength++;
i++;
}
dest.append(source.charAt(i));
dest.append(runLength);
}
return dest.toString();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String example = "aaaaaaBBBBccc";
System.out.println("Encode::"+encode(example));
}
}

java Run-length encoding

I have no idea how to start my assignment.
We got to make a Run-length encoding program,
for example, the users enters this string:
aaaaPPPrrrrr
is replaced with
4a3P5r
Can someone help me get started with it?
Hopefully this will get you started on your assignment:
The fundamental idea behind run-length encoding is that consecutively occurring tokens like aaaa can be replaced by a shorter form 4a (meaning "the following four characters are an 'a'"). This type of encoding was used in the early days of computer graphics to save space when storing an image. Back then, video cards supported a small number of colors and images commonly had the same color all in a row for significant portions of the image)
You can read up on it in detail on Wikipedia
http://en.wikipedia.org/wiki/Run-length_encoding
In order to run-length encode a string, you can loop through the characters in the input string. Have a counter that counts how many times you have seen the same character in a row. When you then see a different character, output the value of the counter and then the character you have been counting. If the value of the counter is 1 (meaning you only saw one of those characters in a row) skip outputting the counter.
public String runLengthEncoding(String text) {
String encodedString = "";
for (int i = 0, count = 1; i < text.length(); i++) {
if (i + 1 < text.length() && text.charAt(i) == text.charAt(i + 1))
count++;
else {
encodedString = encodedString.concat(Integer.toString(count))
.concat(Character.toString(text.charAt(i)));
count = 1;
}
}
return encodedString;
}
Try this one out.
This can easily and simply be done using a StringBuilder and a few helper variables to keep track of how many of each letter you've seen. Then just build as you go.
For example:
static String encode(String s) {
StringBuilder sb = new StringBuilder();
char[] word = s.toCharArray();
char current = word[0]; // We initialize to compare vs. first letter
// our helper variables
int index = 0; // tracks how far along we are
int count = 0; // how many of the same letter we've seen
for (char c : word) {
if (c == current) {
count++;
index++;
if (index == word.length)
sb.append(current + Integer.toString(count));
}
else {
sb.append(current + Integer.toString(count));
count = 1;
current = c;
index++;
}
}
return sb.toString();
}
Since this is clearly a homework assignment, I challenge you to learn the approach and not just simply use the answer as the solution to your homework. StringBuilders are very useful for building things as you go, thus keeping your runtime O(n) in many cases. Here using a couple of helper variables to track where we are in the iteration "index" and another to keep count of how many of a particular letter we've seen "count", we keep all necessary info for building our encoded string as we go.
Try this out:
private static String encode(String sampleInput) {
String encodedString = null;
//get the input to a character array.
// String sampleInput = "aabbcccd";
char[] charArr = sampleInput.toCharArray();
char prev=(char)0;
int counter =1;
//compare each element with its next element and
//if same increment the counter
StringBuilder sb = new StringBuilder();
for (int i = 0; i < charArr.length; i++) {
if(i+1 < charArr.length && charArr[i] == charArr[i+1]){
counter ++;
}else {
//System.out.print(counter + Character.toString(charArr[i]));
sb.append(counter + Character.toString(charArr[i]));
counter = 1;
}
}
return sb.toString();
}
Here is my solution in java
public String encodingString(String s){
StringBuilder encodedString = new StringBuilder();
List<Character> listOfChars = new ArrayList<Character>();
Set<String> removeRepeated = new HashSet<String>();
//Adding characters of string to list
for(int i=0;i<s.length();i++){
listOfChars.add(s.charAt(i));
}
//Getting the occurance of each character and adding it to set to avoid repeated strings
for(char j:listOfChars){
String temp = Integer.toString(Collections.frequency(listOfChars,j))+Character.toString(j);
removeRepeated.add(temp);
}
//Constructing the encodingString.
for(String k:removeRepeated){
encodedString.append(k);
}
return encodedString.toString();
}
import java.util.Scanner;
/**
* #author jyotiv
*
*/
public class RunLengthEncoding {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Enter line to encode:");
Scanner s=new Scanner(System.in);
String input=s.nextLine();
int len = input.length();
int i = 0;
int noOfOccurencesForEachChar = 0;
char storeChar = input.charAt(0);
String outputString = "";
for(;i<len;i++)
{
if(i+1<len)
{
if(input.charAt(i) == input.charAt(i+1))
{
noOfOccurencesForEachChar++;
}
else
{
outputString = outputString +
Integer.toHexString(noOfOccurencesForEachChar+1) + storeChar;
noOfOccurencesForEachChar = 0;
storeChar = input.charAt(i+1);
}
}
else
{
outputString = outputString +
Integer.toHexString(noOfOccurencesForEachChar+1) + storeChar;
}
}
System.out.println("Encoded line is: " + outputString);
}
}
I have tried this one. It will work for sure.

Categories

Resources