How to solve this java problem efficiently [closed] - java

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last year.
Improve this question
I am trying to solve this problem and I worked out a solution but its too slow, this is my approach:
loop to add all strings in an array
loop to concatenate strings based on inputs
print out final concatenated string
Which works, but I need a faster approach. I am thinking of trying to determine the order of concatenation of each string, then just print strings based on the order. But I have no idea how to determine the order in the first place. Does anyone have any advice? Thank you!
[SOLVED] thank you guys for the help! I was able to pass the test cases.

Don’t implement this task literally.
The entire task is designed such that you end up with all N input strings being concatenated in one string, the order determined by the numbers read from System.in.
But the result is not the concatenated string. The result is the output you produce by printing this concatenated string. So you get the same result, i.e. the same output, when you just print the original strings (without separators or line breaks) in the correct order, as if they were concatenated.
Kattio io = new Kattio(System.in, System.out);
int numStrs = io.getInt();
final class StringNode {
final String s;
StringNode last = this, next;
StringNode(String s) {
this.s = s;
}
void append(StringNode s) {
last.next = s;
last = s.last;
}
}
StringNode[] array = new StringNode[numStrs];
for(int i = 0; i < numStrs; i++) array[i] = new StringNode(io.getWord());
int idx = 0;
for(int j = 0; j < numStrs - 1; j++) {
int a = io.getInt() - 1, b = io.getInt() - 1;
array[a].append(array[b]);
idx = a;
}
for(StringNode n = array[idx]; n != null; n = n.next) System.out.print(n.s);
System.out.println();
The main issue with performing the string concatenation literally, is the copying of the characters, potentially over and over again, depending on the given order. In the worst case, you get an order which let’s you copy again the same data you’ve just copied, for every step, ending up at O(N²) time complexity.
The approach above skips the entire task of creating new strings or copying characters, making each step as cheap as two variable assignments.
Note that even if you’re going back to implement the task literally, i.e. to produce a single String, you can do it by replacing the final printing loop by a loop which appends all strings to a single StringBuilder. This will still be a linear operation, as appending all strings in the already determined final order implies copying each string only once, instead of repeatedly.
But as long as the success is measured by the output written to System.out, you don’t need to construct a final String object.

Java's strings are immutable. This means that each concatenation results in a fully new copy where both a and b are copied.
You need to use Java's StringBuilder and convert the end result to a normal string. StringBuilders truely append one to the other without copying the first one. They work like a dynamic length array under the hood.

Related

java I thought strings were immutable

I was always told strings in java are immutable, unless your going to use the string builder class or string writter class.
Take a look at this practice question I found online,
Given a string and a non-negative int n, return a larger string that is n copies of the original string.
stringTimes("Hi", 2) → "HiHi"
stringTimes("Hi", 3) → "HiHiHi"
stringTimes("Hi", 1) → "Hi"
and the solution came out to be
Solution:
public String stringTimes(String str, int n) {
String result = "";
for (int i=0; i<n; i++) {
result = result + str; // could use += here
}
return result;
}
As you see in the solution our 3rd line assigns the string , then we change it in our for loop. This makes no sense to me! (I answered the question in another nooby way ) Once I saw this solution I knew I had to ask you guys.
Thoughts? I know im not that great at programming but I haven't seen this type of example here before, so I thought I'd share.
The trick to understanding what's going on is the line below:
result = result + str;
or its equivalent
result += str;
Java compiler performs a trick on this syntax - behind the scene, it generates the following code:
result = result.concat(str);
Variable result participates in this expression twice - once as the original string on which concat method is called, and once as the target of an assignment. The assignment does not mutate the original string, it replaces the entire String object with a new immutable one provided by concat.
Perhaps it would be easier to see if we introduce an additional String variable into the code:
String temp = result.concat(str);
result = temp;
Once the first line has executed, you have two String objects - temp, which is "HiHi", and result, which is still "Hi". When the second line is executed, result gets replaced with temp, acquiring a new value of "HiHi".
If you use Eclipse, you could make a breakpoint and run it step by step. You will find the id (find it in "Variables" View) of "result" changed every time after java did
result = result + str;
On the other hand, if you use StringBuffer like
StringBuffer result = new StringBuffer("");
for(int i = 0; i < n; i++){
result.append(str);
}
the id of result will not change.
String objects are indeed immutable. result is not a String, it is a reference to a String object. In each iteration, a new String object is created and assigned to the same reference. The old object with no reference is eventually destroyed by a garbage collector. For a simple example like this, it is a possible solution. However, creating a new String object in each iteration in a real-world application is not a smart idea.

Inserting letters into a string at every possible spot [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am looking to insert a single letter one by one into every possible index of a string.
For example the string ry
Would go "ary" "bry" "cry" ... "zry" ... "ray" "rby" .... "rzy" ... "rya" "ryb"
I am not sure how to begin, any help?
Try this
System.out.println("originaltext".replaceAll(".{1}","$0ry"));
The above is using the String replaceAll(String regex, String replacement) method - "Replaces each substring of this string that matches the given regular expression with the given replacement."
".{1}" - The regular expression used to find exactly one occurrence({1}) of any character(.)
"$0ry" - The replacement string with "$0" for the matched value followed by the required characters(ry).
This is repeated for all matches!
Example Code
String originalString = /* your original string */;
char[] characters = /* array of characters you want to insert */;
Vector<String> newStrings = new Vector<>();
String newString;
for (int idx = 0; idx < originalString.length() + 1; ++idx) {
for (char ch : characters) {
newString = originalString.substring(0, idx)
+ ch
+ originalString.substring(idx, originalString.length());
newStrings.add(newString);
}
}
Explanation
Processing all cases:
In order to insert every single letter into an index in a string, you need a loop to iterate through every letter.
In order to insert a letter into every index in a string, you need a loop to iterate through every index in the string.
To do both at once, you should nest one loop inside the other. That way, every combination of an index and a character will be processed. In the problem you presented, it does not matter which loop goes inside the other--it will work either way.
(you actually have to iterate through every index in the string +1... I explain why below)
Forming the new string:
First, it is important to note the following:
What you want to do is not "insert a character into an index" but rather "insert a character between two indices". The distinction is important because you do not want to replace the previous character at that index, but rather move all characters starting at that index to the right by one index in order to make room for a new character "at that index."
This is why you must iterate through every index of the original string plus one. Because once you "insert" the character, the length of the new string is actually equal to originalString.length() + 1, i.e. there are n + 1 possible locations where you can "insert" the character.
Considering this, the way you actually form a new string (in the way you want to) is by getting everything to the left of your target index, getting everything to the right of your target index, and then concatenating them with the new character in between, e.g. leftSubstring + newCharacter + rightSubstring.
Now, it might seem that this would not work for the very first and very last index, because the leftSubstring and/or rightSubstring would be an empty string. However, string concatenation still works even with an empty string.
Notes about Example Code
characters can also be any collection that implements iterable. It does not have to be a primitive array.
characters does not have to contain primitive char elements. It may contain any type that can be concatenated with a String.
Note that the substring(int,int) method of String returns the substring including the character at beginIndex but not including the character at endIndex. One implication of this is that endIndex may be equal to string.length() without any problems.
Well you will need a loop for from i = 0 to your string's length
Then another loop for every character you want to insert - so if you want to keep creating new strings with every possible letter from A to Z make a loop from char A = 'a' to 'z' and keep increasing them ++A(this should work in java).
This should give you some ideas.
Supposing, for instance, you want them all in a list, you could do something like that (iterating all places to insert and iterating over all letters):
List<String> insertLetters(String s) {
List<String> list = new ArrayList<String>();
for (int i = 0; i <= s.length(); i++) {
String prefix = s.substring(0, i);
String postfix = s.substring(i, s.length());
for (char letter = 'a'; letter <= 'z'; letter++) {
String newString = prefix + letter + postfix;
list.add(newString);
}
}
return list;
}
String x = "ry";
char[] alphabets = "abcdefghijklmnopqrstuvwxyz".toCharArray();
String[] alphabets2 = new String[alphabets.length];
for (int i=0;i<alphabets.length;i++){
char z = alphabets[i];
alphabets2[i] = z + x;
}
for (String s: alphabets2
) {
System.out.println(s);
}
First of all you would need a Array of alphabet (Easier for you to continue).
I will not give you exact answer, but something to start, so you would learn.
int length = 0;
Arrays here
while(length <= 2) {
think what would be here: hint you put to index (length 0) the all alphabet and move to index 1 and then 2
}

How can I efficiently use StringBuilder?

In the past, I've always used printf to format printing to the console but the assignment I currently have (creating an invoice report) wants us to use StringBuilder, but I have no idea how to do so without simply using " " for every gap needed. For example... I'm supposed to print this out
Invoice Customer Salesperson Subtotal Fees Taxes Discount Total
INV001 Company Eccleston, Chris $ 2357.60 $ 40.00 $ 190.19 $ -282.91 $ 2304.88
But I don't know how to get everything to line up using the StringBuilder. Any advice?
StringBuilder aims to reduce the overhead associated with creating strings.
As you may or may not know, strings are immutable. What this means that something like
String a = "foo";
String b = "bar";
String c = a + b;
String d = c + c;
creates a new string for each line. If all we are concerned about is the final string d, the line with string c is wasting space because it creates a new String object when we don't need it.
String builder simply delays actually building the String object until you call .toString(). At that point, it converts an internal char[] to an actual string.
Let's take another example.
String foo() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++)
sb.append(i);
return sb.toString();
}
Here, we only create one string. StringBuilder will keep track of the chars you have added to your string in its internal char[] value. Note that value.length will generally be larger than the total chars you have added to your StringBuilder, but value might run out of room for what you're appending if the string you are building gets too big. When that happens, it'll resize, which just means replacing value with a larger char[], and copying over the old values to the new array, along with the chars of whatever you appended.
Finally, when you call sb.toString(), the StringBuilder will call a String constructor that takes an argument of a char[].
That means only one String object was created, and we only needed enough memory for our char[] and to resize it.
Compare with the following:
String foo() {
String toReturn = "";
for (int i = 0; i < 100; i++)
toReturn += "" + i;
toReturn;
}
Here, we have 101 string objects created (maybe more, I'm unsure). We only needed one though! This means that at every call, we're disposing the original string toReturn represented, and creating another string.
With a large string, especially, this is very expensive, because at every call you need to first acquire as much memory as the new string needs, and dispose of as much memory as the old string had. It's not a big deal when things are kept short, but when you're working with entire files this can easily become a problem.
In a nutshell: if you're working appending / removing information before finalizing an output: use a StringBuilder. If your strings are very short, I think it is OK to just concatenate normally for convenience, but this is up to you to define what "short" is.

Best way to modify an existing string? StringBuilder or convert to char array and back to string?

I'm learning Java and am wondering what's the best way to modify strings here (both for performance and to learn the preferred method in Java). Assume you're looping through a string and checking each character/performing some action on that index in the string.
Do I use the StringBuilder class, or convert the string into a char array, make my modifications, and then convert the char array back to a string?
Example for StringBuilder:
StringBuilder newString = new StringBuilder(oldString);
for (int i = 0; i < oldString.length() ; i++) {
newString.setCharAt(i, 'X');
}
Example for char array conversion:
char[] newStringArray = oldString.toCharArray();
for (int i = 0; i < oldString.length() ; i++) {
myNameChars[i] = 'X';
}
myString = String.valueOf(newStringArray);
What are the pros/cons to each different way?
I take it that StringBuilder is going to be more efficient since the converting to a char array makes copies of the array each time you update an index.
I say do whatever is most readable/maintainable until you you know that String "modification" is slowing you down. To me, this is the most readable:
Sting s = "foo";
s += "bar";
s += "baz";
If that's too slow, I'd use a StringBuilder. You may want to compare this to StringBuffer. If performance matters and synchronization does not, StringBuilder should be faster. If sychronization is needed, then you should use StringBuffer.
Also it's important to know that these strings are not being modified. In java, Strings are immutable.
This is all context specific. If you optimize this code and it doesn't make a noticeable difference (and this is usually the case), then you just thought longer than you had to and you probably made your code more difficult to understand. Optimize when you need to, not because you can. And before you do that, make sure the code you're optimizing is the cause of your performance issue.
What are the pros/cons to each different way. I take it that StringBuilder is going to be more efficient since the convering to a char array makes copies of the array each time you update an index.
As written, the code in your second example will create just two arrays: one when you call toCharArray(), and another when you call String.valueOf() (String stores data in a char[] array). The element manipulations you are performing should not trigger any object allocations. There are no copies being made of the array when you read or write an element.
If you are going to be doing any sort of String manipulation, the recommended practice is to use a StringBuilder. If you are writing very performance-sensitive code, and your transformation does not alter the length of the string, then it might be worthwhile to manipulate the array directly. But since you are learning Java as a new language, I am going to guess that you are not working in high frequency trading or any other environment where latency is critical. Therefore, you are probably better off using a StringBuilder.
If you are performing any transformations that might yield a string of a different length than the original, you should almost certainly use a StringBuilder; it will resize its internal buffer as necessary.
On a related note, if you are doing simple string concatenation (e.g, s = "a" + someObject + "c"), the compiler will actually transform those operations into a chain of StringBuilder.append() calls, so you are free to use whichever you find more aesthetically pleasing. I personally prefer the + operator. However, if you are building up a string across multiple statements, you should create a single StringBuilder.
For example:
public String toString() {
return "{field1 =" + this.field1 +
", field2 =" + this.field2 +
...
", field50 =" + this.field50 + "}";
}
Here, we have a single, long expression involving many concatenations. You don't need to worry about hand-optimizing this, because the compiler will use a single StringBuilder and just call append() on it repeatedly.
String s = ...;
if (someCondition) {
s += someValue;
}
s += additionalValue;
return s;
Here, you'll end up with two StringBuilders being created under the covers, but unless this is an extremely hot code path in a latency-critical application, it's really not worth fretting about. Given similar code, but with many more separate concatenations, it might be worth optimizing. Same goes if you know the strings might be very large. But don't just guess--measure! Demonstrate that there's a performance problem before you try to fix it. (Note: this is just a general rule for "micro optimizations"; there's rarely a downside to explicitly using a StringBuilder. But don't assume it will make a measurable difference: if you're concerned about it, you should actually measure.)
String s = "";
for (final Object item : items) {
s += item + "\n";
}
Here, we're performing a separate concatenation operation on each loop iteration, which means a new StringBuilder will be allocated on each pass. In this case, it's probably worth using a single StringBuilder since you may not know how large the collection will be. I would consider this an exception to the "prove there's a performance problem before optimizing rule": if the operation has the potential to explode in complexity based on input, err on the side of caution.
Which option will perform the best is not an easy question.
I did a benchmark using Caliper:
RUNTIME (NS)
array 88
builder 126
builderTillEnd 76
concat 3435
Benchmarked methods:
public static String array(String input)
{
char[] result = input.toCharArray(); // COPYING
for (int i = 0; i < input.length(); i++)
{
result[i] = 'X';
}
return String.valueOf(result); // COPYING
}
public static String builder(String input)
{
StringBuilder result = new StringBuilder(input); // COPYING
for (int i = 0; i < input.length(); i++)
{
result.setCharAt(i, 'X');
}
return result.toString(); // COPYING
}
public static StringBuilder builderTillEnd(String input)
{
StringBuilder result = new StringBuilder(input); // COPYING
for (int i = 0; i < input.length(); i++)
{
result.setCharAt(i, 'X');
}
return result;
}
public static String concat(String input)
{
String result = "";
for (int i = 0; i < input.length(); i++)
{
result += 'X'; // terrible COPYING, COPYING, COPYING... same as:
// result = new StringBuilder(result).append('X').toString();
}
return result;
}
Remarks
If we want to modify a String, we have to do at least 1 copy of that input String, because Strings in Java are immutable.
java.lang.StringBuilder extends java.lang.AbstractStringBuilder. StringBuilder.setCharAt() is inherited from AbstractStringBuilder and looks like this:
public void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
value[index] = ch;
}
AbstractStringBuilder internally uses the simplest char array: char value[]. So, result[i] = 'X' is very similar to result.setCharAt(i, 'X'), however the second will call a polymorphic method (which probably gets inlined by JVM) and check bounds in if, so it will be a bit slower.
Conclusions
If you can operate on StringBuilder until the end (you don't need String back) - do it. It's the preferred way and also the fastest. Simply the best.
If you want String in the end and this is the bottleneck of your program, then you might consider using char array. In benchmark char array was ~25% faster than StringBuilder. Be sure to properly measure execution time of your program before and after optimization, because there is no guarantee about this 25%.
Never concatenate Strings in the loop with + or +=, unless you really know what you do. Usally it's better to use explicit StringBuilder and append().
I'd prefer to use StringBuilder class where original string is modified.
For String manipulation, I like StringUtil class. You'll need to get Apache commons dependency to use it

The time complexity for a code segment

From an online notes, I read the following java code snippet for reversing a string, which is claimed to have quadratic time complexity. It seems to me that the “for” loop for i just iterates the whole length of s. How does it cause a quadratic time complexity?
public static String reverse(String s)
{
String rev = new String();
for (int i = (s.length()-1); i>=0; i--) {
rev = rev.append(s.charAt(i));
}
return rev.toString();
}
public static String reverse(String s)
{
String rev = " ";
for (int i=s.length()-1; i>=0; i--)
rev.append(s.charAt(i); // <--------- This is O(n)
Return rev.toString();
}
I copy pasted your code. I'm not sure where you get this but actually String doesn't have append method. Maybe rev is a StringBuilder or another Appendable.
Possibly because the append call does not execute in constant time. If it's linear with the length of the string, that would explain it.
append has to find the end of the string, which is Ο(n). So, you have an Ο(n) loop executed Ο(n) times.
I don't think String has an append method. So, this code won't compile.
But, coming to the problem of quadratic complexity, let us assume that you are actually appending the string with a character using '+' operator or the String.concat() method.
The String objects are immutable. So, whenever you append to a string, a new string of bigger length is created, old string contents are copied to it and then the final character is appended, and the previous string is destroyed. So, this process takes more and more time as the string grows.
The appending loop takes O(n) time but for every loop you take O(n) time to copy the string character by character. This leads to quadratic complexity.
It would be better to use StringBuilder or StringBuffer. However, I guess the time complexity you mentioned would be with older java compilers. But, new advanced compilers would actually optimize the '+' operation with StringBuilder.

Categories