compress string into a2b3... etc [duplicate] - java

This question already has answers here:
Turn String aaaabbbbddd into a4b4d3
(7 answers)
Closed 8 years ago.
Just brushing up on some old java techniques. Currently working through a set of problems, and the one im on is compress strings in the format of aabbcccDDDDeff to a2b2c3d4e1f2. Something funky is happening in my code, pls help sort it out:
public static void main(String[] args) {
String c = "aabbCCCCCdfff";
System.out.println(compress(c));
}
public static String compress(String s) {
String ns = "";
int count = 0;
char temp = 0;
for (int x = 0; x < s.length(); x++) {
if (x == 0) {
ns.concat(String.valueOf(s.charAt(x)));
temp = s.charAt(x);
count++;
} else if (temp == s.charAt(x)) {
count++;
} else {
ns.concat(String.valueOf(count));
count = 0;
ns.concat(String.valueOf(s.charAt(x)));
temp = s.charAt(x);
}
}
return ns;
}
the output is just appearing as null. i'd like to keep my same logic

String.concat (String#concat docs) doesn't mutate your string, it returns a new string that you need to assign to your string variable
ns = ns.concat(theOtherString);
and not this (which is essentially a no-op)
ns.concat(theOtherString);
for example:
ns = ns.concat(String.valueOf(s.charAt(x)));
I would recommend using StringBuilder with its append method for multiple string concatenations. If you choose not to then that is fine if you can argue why the performance benefit doesn't exist, or exists but doesn't apply, in your use case.

Strings in Java are immutable. String.concat does not change the String it's called on, it returns a new String which is the concatination of the object it's called on and the parameter. If you want to accumulate strings, you'd be better served to use a StringBuilder:
StringBuilder ns = new StringBuilder();
int count = 0;
char temp = 0;
for (int x = 0; x < s.length(); x++) {
if (x == 0) {
ns.append(s.charAt(x));
temp = s.charAt(x);
count++;
// rest of code...

Related

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

Code to build all possible strings, and find a specific one doesen't work as expected [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 3 years ago.
Part of a task I'm trying to solve is building all possible combinations of letters and whitespace. I'm trying to find a specific String, while building the combinations. I think i implemented the solution well, but my test tells me otherwise.
I'm taking the chars from an array and in a for loop I build a String from them, then print the solution. When I built all the combinations of let's say "1 character combinations", then i move on to "2 character combinations". I have a boolean to check if the contents of the StringBuilder is equal to the given String that i want to find. I checked, and the example String was indeed built, but the boolean didn't change.
public static void main(String[] args) throws InterruptedException {
findString("rxc");
}
public static void findString(String string) {
boolean isFound = false;
String allChars = "abcdefghijklmnopqrstuvwxyz ";
char[] sequence = allChars.toCharArray();
int lengthOfExpression = 3;
//builder contains 3 whitespaces at the beginning
StringBuilder builder = new StringBuilder(" ");
int[] pos = new int[lengthOfExpression];
int total = (int) Math.pow(allChars.length(), lengthOfExpression);
for (int i = 0; i < total; i++) {
for (int x = 0; x < lengthOfExpression; x++) {
if (pos[x] == sequence.length) {
pos[x] = 0;
if (x + 1 < lengthOfExpression) {
pos[x + 1]++;
}
}
builder.setCharAt(x, sequence[pos[x]]);
}
pos[0]++;
if (builder.toString() == string) {
isFound = true;
}
System.out.println(builder.toString());
}
System.out.println(isFound);
}
Expected result would be a 'true' printed at the end. Instead, my result is as follows:
//lots of lines of combinations omitted
r
s
t
u
v
w
x
y
z
false
Do not compare strings with ==, use .equals() instead; so for your code:
Change the if (builder.toString() == string) to if (builder.toString().equals(string))

How to get better performance with the following code

I'm trying to get this code to run faster. With big strings it takes too much time. I don't know much about multithreading but i'd like to try to divide it into threads to help get faster results but i don't really get how to do it.
Where do i start and what can i do?
The following code looks throught the string trying to find substrings which are put in manually.
First line asks for the string.
Second line asks for a number of strings that you'll try to find,
then you input the strings that you're looking for that are no longer than 4 and no shorter than 1.
In the output you get how many times a substring was in the string.
BufferedReader br;
br = new BufferedReader(new InputStreamReader(System.in));
String getLine = br.readLine();
if (getLine.length() < 1 || getLine.length() > 1000000) {
getLine = br.readLine();
}
getLine = getLine.toUpperCase();
String helpNumber = br.readLine();
int number = Integer.parseInt(helpNumber);
if(number < 1 || number > 100000){
helpNumber = br.readLine();
number = Integer.parseInt(helpNumber);
}
String[] shortCuts = new String[number];
for (int i = 0; i < number; i++) {
shortCuts[i] = br.readLine();
if(shortCuts[i].length()>=1 && shortCuts[i].length()<=4) {
shortCuts[i] = shortCuts[i];
shortCuts[i] = shortCuts[i].toUpperCase();
}else i--;
}
for (int i = 0; i < number; i++) {
System.out.println(count(getLine, shortCuts[i]));
}
}
private static int count(final String string, final String substring) {
int count = 0;
int index = 0;
while ((index = string.indexOf(substring, index)) != -1) {
index++;
count++;
}
return count;
}
I want to make this code run faster.
Assuming the 'slow' part of the code is the counting of the sub string in the String invoked here
for (int i = 0; i < number; i++) {
System.out.println(count(getLine, shortCuts[i]));
}
A thought to speed up the following operation
private static int count(final String string, final String substring)
is to invoke it into number of logical processor in your system. You'll need to understand and read about CompletableFuture
Hence, you'll be able to break down the array shortcuts size n into partition depending on the logical processor in your machine Runtime.getRuntime().availableProcessors(); Combine the result in the end and return when all thread reach completion.

Is converting to String the most succinct way to remove the last comma in output in java?

So basically this is how my code looked like
public static void printPrime(int[] arr)
{
int len = arr.length;
for(int i = 0; i < len; i++)
{
int c = countFactor(arr[i]);
if(c == 2)
{
System.out.print(arr[i] + ",");
}
}
}
So the output did have the 'comma' in the end. I tried looking around to remove the last comma some answers say to print last element separately but that can only happen when output depends on the for loop and not the if condition.
But as you can see I don't know how many elements I am going to get from the if condition. Only two things I can think of, to add another loop or use String then substr to output.
So I converted it to String
public static void printPrime(int[] arr)
{
int len = arr.length;
String str = "";
for(int i = 0; i < len; i++)
{
int c = countFactor(arr[i]);
if(c == 2)
{
str = str + arr[i] + ",";
}
}
str = str.substring(0, str.length()-1);
System.out.println(str);
}
My question is about knowing the optimum way (converting to string then substringing it?) for similar questions or could there be a better way as well? That I seem to be missing.
You don't have to construct a string. Consider the following slight tweaks:
public static void printPrime(int[] arr)
{
int len = arr.length;
String sep = ""; // HERE
for(int i = 0; i < len; i++)
{
int c = countFactor(arr[i]);
if(c == 2)
{
System.out.print(sep); // HERE
sep = ",";
System.out.print(arr[i]);
}
}
}
Print the delimiter first, and store its value in a variable: the first time it's printed, it will print the empty string. Thereafter, it prints the comma.
Whatever means you use should operate correctly for an empty array (length 0), a singleton array (length 1) and a long array (a large length).
Adding the comma then removing it requires special case handling for the empty array case. So you must have conditional code (an if statement) whatever you do.

Substring alternative

So I'm creating a program that will output the first character of a string and then the first character of another string. Then the second character of the first string and the second character of the second string, and so on.
I created what is below, I was just wondering if there is an alternative to this using a loop or something rather than substring
public class Whatever
{
public static void main(String[] args)
{
System.out.println (interleave ("abcdefg", "1234"));
}
public static String interleave(String you, String me)
{
if (you.length() == 0) return me;
else if (me.length() == 0) return you;
return you.substring(0,1) + interleave(me, you.substring(1));
}
}
OUTPUT: a1b2c3d4efg
Well, if you really don't want to use substrings, you can use String's toCharArray() method, then you can use a StringBuilder to append the chars. With this you can loop through each of the array's indices.
Doing so, this would be the outcome:
public static String interleave(String you, String me) {
char[] a = you.toCharArray();
char[] b = me.toCharArray();
StringBuilder out = new StringBuilder();
int maxLength = Math.max(a.length, b.length);
for( int i = 0; i < maxLength; i++ ) {
if( i < a.length ) out.append(a[i]);
if( i < b.length ) out.append(b[i]);
}
return out.toString();
}
Your code is efficient enough as it is, though. This can be an alternative, if you really want to avoid substrings.
This is a loop implementation (not handling null value, just to show the logic):
public static String interleave(String you, String me) {
StringBuilder result = new StringBuilder();
for (int i = 0 ; i < Math.max(you.length(), me.length()) ; i++) {
if (i < you.length()) {
result.append(you.charAt(i)); }
if (i < me.length()) {
result.append(me.charAt(i));
}
}
return result.toString();
}
The solution I am proposing is based on the expected output - In your particular case consider using split method of String since you are interleaving by on character.
So do something like this,
String[] xs = "abcdefg".split("");
String[] ys = "1234".split("");
Now loop over the larger array and ensure interleave ensuring that you perform length checks on the smaller one before accessing.
To implement this as a loop you would have to maintain the position in and keep adding until one finishes then tack the rest on. Any larger sized strings should use a StringBuilder. Something like this (untested):
int i = 0;
String result = "";
while(i <= you.length() && i <= me.length())
{
result += you.charAt(i) + me.charAt(i);
i++;
}
if(i == you.length())
result += me.substring(i);
else
result += you.substring(i);
Improved (in some sense) #BenjaminBoutier answer.
StringBuilder is the most efficient way to concatenate Strings.
public static String interleave(String you, String me) {
StringBuilder result = new StringBuilder();
int min = Math.min(you.length(), me.length());
String longest = you.length() > me.length() ? you : me;
int i = 0;
while (i < min) { // mix characters
result.append(you.charAt(i));
result.append(me.charAt(i));
i++;
}
while (i < longest.length()) { // add the leading characters of longest
result.append(longest.charAt(i));
i++;
}
return result.toString();
}

Categories

Resources