Executing code N times and other code N+1 times - java

The question is about while-loops in which I need some code to be executed N times and some other code N+1 times. NOT about concatening Strings, I just use this as bad-coded yet short example.
Let me explain my question by providing an example.
Say I want to concatenate N+1 Strings, by glueing them with "\n", for example. I will have N+1 lines of text then, but I only need to add N times "\n".
Is there any boilerplate solution for this type of loop in which you have to execute some code N times and other code N+1 times? I'm NOT asking for solution to concatenate Strings! That is just a (bad) example. I'm looking for the general solution.
The problem I have with this is code duplication, so to code my example I'll do this (bad pseudo code, I know I have to use StringBuilder etc.):
String[] lines = <some array of dimension N+1>;
String total = lines[0];
for (int i = 1; i < N + 1; i++){
total += "\n" + lines[i];
}
The problem becomes worse if the code that has to be executed N+1 times, becomes larger, of course. Then I would do something like
codeA(); // adding the line of text
for (int i = 1; i < N + 1; i++){
codeB(); // adding the "\n"
codeA();
}
To remove the duplication, you can do this different by checking inside the loop, too, but then I find this quite stupid as I know beforehand that the check is pre-determined, as it will only be false the first iteration:
for (int i = 0; i < N + 1; i++){
if (i > 0){
codeB(); // adding the "\n"
}
codeA();
}
Is there any solution for this, a sort of while-loop that initializes once with codeA() en then keeps looping over codeB() and codeA()?
People must have run into this before, I guess. Just wondering if there are any beautiful solutions for this.

To my dissapointment, I believe that there is no such construct that satisfies the conditions as you have stated them and I will attempt to explain why (though I can't prove it in a strictly mathematical way).
The requirements of the problem are:
We have two parts of code: codeA() and codeB()
The two parts are executed a different number of times, N and N+1
We want to avoid adding a condition inside the loop
We want to execute each part only as many times as strictly necessary
2) is a direct consequence of 1). If we didn't have two parts of code we would not need a different number of executions. We would have a single loop body.
4) is again a consequence of 1). There is no redundant execution if we have a single loop body. We can control its execution through the loop's condition
So the restrictions are basically 1) and 3).
Now inside the loop we need to answer two questions on each iteration: a) do we execute codeA()? and b) do we execute codeB()? We simply do not have enough information to decide since we only have a single condition (the condition of the loop) and that condition will be used to decide if both of the code parts would be executed or not.
So we need to break 1) and/or 3) Either we add the extra condition inside the loop or we delegate the decision to some other code (thus not having two parts anymore).
Apparently an example of delegation could be (I am using the string concatenation example):
String [] lines = ...
for (int i = 0; i < N; i++){
// delegate to a utility class LineBuilder (perhaps an extension of StringBuilder) to concatenate lines
// this class would still need to check a condition e.g. for the first line to skip the "\n"
// since we have delegated the decisions we do not have two code parts inside the loop
lineBuilder.addLine( lines[i] );
}
Now a more interesting case of delegation would be if we could delegate the decision to the data itself (this might worth keeping in mind). Example:
List<Line> lines = Arrays.asList(
new FirstLine("Every"), // note this class is different
new Line("word"),
new Line("on"),
new Line("separate"),
new Line("line") );
StringBuffer sb = new StringBuffer();
for (Line l : lines) {
// Again the decision is delegated. Data knows how to print itself
// Line would return: "\n" + s
// FirstLine would return: s
sb.append( l.getPrintVersion() );
}
Of course all of the above does not mean that you couldn't implement a class that tries to solve the problem. I believe though this is beyond the scope of your original question not to mention that would be an overkill for simple loops

Concatenating Strings like this is a bad idea and a much bigger issue IMHO.
However to answer your question I would do
String sep = "";
StringBuilder sb= new StringBuilder();
for(String s: lines) {
sb.append(sep).append(s);
sep = "\n";
}
String all = sb.toString();
Note: there is usually a good way to avoid needing to create this String at all such a processing the lines as you get them. It is hard to say without more context.

This kind of thing is fairly common, like when you build sql. This is the pattern that I follow:
String[] lines ...//init somehow;
String total = lines[0];
boolean firstTime = true;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++){
if(firstTime) firstTime = false;
else sb.append('\n');
sb.append(lines[i]);
}
Note that this is not the same, as the first example and here is why:
String[] lines = <some array of dimension N+1>;
String total = lines[0];
for (int i = 1; i < N + 1; i++){
total += "\n" + lines[i];
}
Assuming you have an array of [0] = 'line1' and [1] = 'line2'
Here you end up with line1line2\n, when the desired output is:
line1\nline2.
The example I provided is clear, and does not perform poorly. In fact a much bigger performance gain is made by utilizing StringBuilder/Buffer. Having clear code is essential for the pro.

Personally i have most of the time the same problem, on the String example i use the StringBuilder as you said, and just delete the characters added to much:
StringBuilder sb = new StringBuilder();
for(int i=0; i<N; i++) {
sb.append(array[i]).append("\n");
}
sb.delete(sb.length-1, sb.length); // maybe check if sb contains something
In the common case i suppose there is no other way than adding the if you suggested. To make the code more clear i would check at the end of the for loop:
StringBuilder sb = new StringBuilder();
for(int i=0; i<N; i++) {
sb.append(array[i]);
if(i < N) {
sb.append("\n");
}
}
But i totally agree this is sad to have this double logic

Related

How do you replace multiple words in a string at once in Java?

I am new to Java and am trying to make a program that will take a string 'names' and replace the names of the people mentioned in that string with a nickname from another string 'replacement'. For instance, "John and Robert were on the way to meet Elizabeth" should return "Joe and Bob were on the way to meet Liz".
My issue is that whenever I run my program, I get multiple sentences back with only one name replaced in each (I'm guessing it is creating a new string each time). Is there a way to avoid this and have it all printed in one sentence? Could I use regex on 'replacement' somehow to fix it?
public static string namereplacer(String names, String replacement) {
String[] toreplace = replacement.split("\t|\n");
for (int i = 0; i <= toreplace.length/2; i++) {
if (i % 2 == 0) { //take even values
fixed; += names.replaceAll(toreplace[i+1],toreplace[i]);
}
}
return fixed;
}
public static void main (String[] args) {
namereplacer("John and Robert were on the way to meet Elizabeth", "John\tJoe\nRobert\tBob\nElizabeth\tLiz");
}
I couldn't run the code you have submitted because the variable fixed isn't declared and i suspect the line
fixed; += names.replaceAll(toreplace[i+1],toreplace[i]);
is supposed to be
fixed += names.replaceAll(toreplace[i+1],toreplace[i]);
because that would somewhat explain why you are getting multiple sentences, since you are replacing things multiple times and adding the result to fixed.
That being said I have noticed that when you are replacing the names with
names.replaceAll(toreplace[i+1],toreplace[i]); you should be doing
names.replaceAll(toreplace[i],toreplace[i+1]); since you would like to replace John for Joe, Robert for Bob and Elizabeth for Liz, not the other way around (Joe for John). Basically you want to replace the "even" with the "odd" so if you are selecting the even values of i you you like to replace those with i+1.
Also when iterating the array in the for loop with
for (int i = 0; i <= toreplace.length/2; i++)
you should be doing
for (int i = 0; i <= toreplace.length; i++)
because you want all the "evens" not just half of them, i think you might have switched up yourself a bit here with the evens logic and replacing half...
So to make your code work like you intend to you should replace the following lines:
for (int i = 0; i <= toreplace.length/2; i++) for
for (int i = 0; i <= toreplace.length; i++)
fixed; += names.replaceAll(toreplace[i+1],toreplace[i]); for
fixed = fixed.replaceAll(toreplace[i+1],toreplace[i]);
and also define the variable fixed beforehand Sting fixed = names; so that it has the same value as names.
Furthermore you can also improve your code a little bit:
A for is constituted by three statements for(statement1; statement2; stament3)
statement1 is executed a single time before the code block (what's inside the for)
statement2 defines the condition for executing the code block, the for will run while this statement is true
statement3 is executed everytime after the code block has been executed by the for loop
Generaly you will find and write foor loops like for(i=0; i!=foo; i++) where statement1 is used for variable initialization, statement2 for condition and statement3 for increment, but that doens't mean it is all you can do with it, in reality you can write whatever you want/need in those statements.
In you case you have
for (int i = 0; i <= toreplace.length/2; i++) {
if (i % 2 == 0) { //take even values
fixed; += names.replaceAll(toreplace[i+1],toreplace[i]);
}
}
This means you are iterating over the toreplace array, taking the "evens" and replacing them with the "odds" on the fixed string, however inside the for loop you are selecting only half of the iterations you are making with if (i % 2 == 0) and you don't really need to do that since you can just choose to iterate by 2 instead of iterating all of them and only selecting half of them to do the replacement.
To do this you simply declare for (int i = 0; i != toreplace.length; i+=2) or the long way i=i+2 if you prefer, and you no longer need the if statement because you know you will be iteraing over all the even numbers.
Hope this helped you in some way.
Change the method namereplacer to this
public static String namereplacer(String names, String replacement) {
String[] toreplace = replacement.split("\n");
for(String str: toreplace){
String [] keyValue = str.split("\t");
if(keyValue.length != 2)
continue;
names = names.replaceAll(keyValue[0], keyValue[1]);
}
return names;
}

NZEC error in Hackerearth problem in java

I'm trying the solve this hacker earth problem https://www.hackerearth.com/practice/basic-programming/input-output/basics-of-input-output/practice-problems/algorithm/anagrams-651/description/
I have tried searching through the internet but couldn't find the ideal solution to solve my problem
This is my code:
String a = new String();
String b = new String();
a = sc.nextLine();
b = sc.nextLine();
int t = sc.nextInt();
int check = 0;
int againCheck =0;
for (int k =0; k<t; k++)
{
for (int i =0; i<a.length(); i++)
{
char ch = a.charAt(i);
for (int j =0; j<b.length(); j++)
{
check =0;
if (ch != b.charAt(j))
{
check=1;
}
}
againCheck += check;
}
}
System.out.println(againCheck*againCheck);
I expect the output to be 4, but it is showing the "NZEC" error
Can anyone help me, please?
The requirements state1 that the input is a number (N) followed by 2 x N lines. Your code is reading two strings followed by a number. It is probably throwing an InputMismatchException when it attempts to parse the 3rd line of input as a number.
Hints:
It pays to read the requirements carefully.
Read this article on CodeChef about how to debug a NZEC: https://discuss.codechef.com/t/tutorial-how-to-debug-an-nzec-error/11221. It explains techniques such as catching exceptions in your code and printing out a Java stacktrace so that you can see what is going wrong.
1 - Admittedly, the requirements are not crystal clear. But in the sample input the first line is a number.
As I've written in other answers as well, it is best to write your code like this when submitting on sites:
def myFunction():
try:
#MY LOGIC HERE
except Exception as E:
print("ERROR Occurred : {}".format(E))
This will clearly show you what error you are facing in each test case. For a site like hacker earth, that has several input problems in various test cases, this is a must.
Coming to your question, NZEC stands for : NON ZERO EXIT CODE
This could mean any and everything from input error to server earthquake.
Regardless of hacker-whatsoever.com I am going to give two useful things:
An easier algorithm, so you can code it yourself, becuase your algorithm will not work as you expect;
A Java 8+ solution with totally a different algorithm, more complex but more efficient.
SIMPLE ALGORITM
In you solution you have a tipical double for that you use to check for if every char in a is also in b. That part is good but the rest is discardable. Try to implement this:
For each character of a find the first occurence of that character in b
If there is a match, remove that character from a and b.
The number of remaining characters in both strings is the number of deletes you have to perform to them to transform them to strings that have the same characters, aka anagrams. So, return the sum of the lenght of a and b.
NOTE: It is important that you keep track of what you already encountered: with your approach you would have counted the same character several times!
As you can see it's just pseudo code, of a naive algorithm. It's just to give you a hint to help you with your studying. In fact this algorithm has a max complexity of O(n^2) (because of the nested loop), which is generally bad. Now, a better solution.
BETTER SOLUTION
My algorithm is just O(n). It works this way:
I build a map. (If you don't know what is it, to put it simple it's a data structure to store couples "key-value".) In this case the keys are characters, and the values are integer counters binded to the respective character.
Everytime a character is found in a its counter increases by 1;
Everytime a character is found in b its counter decreases by 1;
Now every counter represents the diffences between number of times its character is present in a and b. So, the sum of the absolute values of the counters is the solution!
To implement it actually add an entry to map whenever I find a character for the first time, instead of pre-costructing a map with the whole alphabet. I also abused with lambda expressions, so to give you a very different sight.
Here's the code:
import java.util.HashMap;
public class HackerEarthProblemSolver {
private static final String a = //your input string
b = //your input string
static int sum = 0; //the result, must be static because lambda
public static void main (String[] args){
HashMap<Character,Integer> map = new HashMap<>(); //creating the map
for (char c: a.toCharArray()){ //for each character in a
map.computeIfPresent(c, (k,i) -> i+1); //+1 to its counter
map.computeIfAbsent(c , k -> 1); //initialize its counter to 1 (0+1)
}
for (char c: b.toCharArray()){ //for each character in b
map.computeIfPresent(c, (k,i) -> i-1); //-1 to its counter
map.computeIfAbsent(c , k -> -1); //initialize its counter to -1 (0-1)
}
map.forEach((k,i) -> sum += Math.abs(i) ); //summing the absolute values of the counters
System.out.println(sum)
}
}
Basically both solutions just counts how many letters the two strings have in common, but with different approach.
Hope I helped!

How to make a program to print the series : 1,12,123...12345678910,1234567891011...& so on to the nth term?

//I tried this one but output was wrong for tenth term
import java.io.*;
public class series
{
public static void main(String args[])throws IOException
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n,i,i1=0,s=0,c=0;
System.out.println("Enter the term of the series you want to get");
n=Integer.parseInt(in.readLine());
for (i=1;i<=n;i++)
{
i1=i;
while (i1!=0)
{
c+=1;
i1=i1/10;
}
s=(int)(s*(Math.pow(10,c))+i);
c=0;
System.out.print(s+" ");
}
}
}
I don't know why you are using your current approach. I would go about this by keeping track of the previous term which was printed.
StringBuilder term = new StringBuilder("");
final int N = 20;
for (int i=1; i <= N; ++i) {
term.append(i);
if (i > 1) System.out.print(",");
System.out.print(term.toString());
}
Demo
Edit: The reason I suggest using a string to display each term is that your requirement appears to mainly be one of presentation. That is, you're not actually doing any math with each term, so why not just avoid a numeric type completely, which also avoids things like overflow and potential loss of precision.
Whilst Tim's answer is neat, I think that the exercise is sufficiently basic that StringBuilder is beyond its scope (*).
Instead, you can use a nested for loop:
final int N = 20;
for (int i=1; i <= N; ++i) {
if (i > 1) System.out.print(",");
for (int a = 1; a <= i; ++a) {
System.out.print(a);
}
}
(This is also going to be more memory efficient, since there is no need to keep reallocating the StringBuffer's internal buffer as i increases. But this is really of secondary (or even lesser) concern).
(*) Yes, you could do the same without StringBuilder, just using String concatenation; but that would be inefficient in ways that beginners may not "get", and so it is something that is best just steered around. Nested loops are far more generally useful than string concatenation in whatever form as a concept to get your head around.
The main reason you are getting wrong output after 10th term is Integer Overflow. You can use long to get rid of that to certain more terms but a definitely better solution is as answered using Strings

optimization - converting std input to integer array in java

I want to read each line of input, store the numbers in an int[] array preform some calculations, then move onto my next line of input as fast as possible.
Input (stdin)
2 4 8
15 10 5
12 14 3999 -284 -71
0 -213 18 4 2
0
This is a pure optimization problem and not entirely good practice in the real world as I'm assuming perfect input. I'm interested in how to improve my current method for taking input from stdin and representing it as an integer array. I have seen methods using scanner where they use a getnextint method, however I've read in multiple places scanner is a lot slower than BufferedReader.
Can this taking in of input step be improved?
Current Method
BufferedReader bufferedInput = new BufferedReader(new InputStreamReader(System.in));
String line;
String[] lineArray;
try{
// a line with just "0" indicates end of std input
while((line = bufferedInput.readLine()) != "0"){
lineArray = line.split("\\s+"); // is "\\s+" the optimized regex
int arrlength = lineArray.length;
int[] lineInt = new int[arrlength];
for(int i = 0; i < arrlength; i++){
lineInt[i] = Integer.parseInt(lineArray[i]);
}
// Preform some operations on lineInt, then regenerate a new
// lineInt with inputs from next line of stdin
}
}catch(IOException e){
}
judging from other questions Difference between parseInt and valueOf in java? parseint seems to be the most efficient method for converting strings to integers1. Any enlightenment would be of great help.
Thank you :)
Edit 1: removed GCD information and 'algorithm' tag
Edit 2: (hopefully) made question more concise, grammatical fix ups
First of all, I just want out that it is totally pointless optimizing in your particular example.
For your example, most people would agree that the best solution is not the optimal one. Rather the most readable solution is will be the best.
Having said that, if you want the most optimal solution, then don't use Scanner, don't use BufferedReader.readLine(), don't use String.split and don't use Integer.parseInt(...).
Instead read characters one at a time using BufferedReader.read() and parse and convert them to int by hand. You also need to implement your own "extendable array of int" type that behaves like an ArrayList<Integer>.
This is a lot of (unnecessary) work, and many more lines of code to maintain. BAD IDEA ...
I second what Stephen said, the speed of parsing is likely to massively outperform the speed of actual I/O done, therefore improving parsing won't give you much.
Seriously, don't do this unless you've built the whole system, profiled it and found that inefficient parsing is what keeps it from hitting its performance targets.
But strictly just as an exercise, and because the general principle may be useful elsewhere, here's an example of how to parse it straight from a string.
The assumptions are:
You will use a sensible encoding, where the characters 0..9 are consecutive.
The only characters in the stream will be 0..9, minus sign and space.
All the numbers are well-formed.
Another important caveat is that for the sake of simplicity I used ArrayList, which is a bad idea for storing primitives, the overhead of boxing/unboxing probably wipes out all improvement in parsing speed. In the real world I'd use a list variant custom-made for primitives.
public static List<Integer> parse(String s) {
List<Integer> ret = new ArrayList<Integer>();
int sign = 1;
int current = 0;
boolean inNumber = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= '0' && c <= '9') { //we assume a sensible encoding
current = current * 10 + sign * (c-'0');
inNumber = true;
}
else if (c == ' ' && inNumber) {
ret.add(current);
current = 0;
inNumber = false;
sign = 1;;
}
else if (c == '-') {
sign = -1;
}
}
if (inNumber) {
ret.add(current);
}
return ret;
}

Java loop to collect the second and third elements every three in an array

I have a file with data in the form timestamp, coordinate, coordinate, seperated by spaces, as here;
14:25:01.215 370.0 333.0
I need to loop through and add the coordinates only to an array. The data from the file is read in and put into as String[] called info, from split(" "). I have two problems, I think the end of the file has a extra " " which I need to lose appropriately and I also want confirmation/suggestions of my loop, at the moment I am getting sporadic out of bounds exceptions. My loop is as follows;
String[] info;
info = dataHolder.split(" ");
ArrayList<String> coOrds1 = new ArrayList<String>();
for (int counter = 0; counter < info.length; counter = counter+3)
{
coOrds1.add(info[counter+1]);
coOrds1.add(info[counter+2]);
}
Help and suggestions appreciated.
The text file is here but the class receives in a UDP packet from another class so I am unsure if this potentially adds " " at the end or not.
There are various classes/methods in Google's Guava library that could help with this task, in particular Splitter.omitEmptyStrings() which will discard any trailing space at the end of the file:
String input = Files.toString(file, Charsets.US_ASCII);
Iterable<String> fields =
Splitter.on(" ")
.omitEmptyStrings()
.split(input);
List<Coord> coords = Lists.newArrayList();
for (List<String> group: Iterables.partition(fields, 3)) {
String t = group.get(0);
double x = Double.parseDouble(group.get(1));
double y = Double.parseDouble(group.get(2));
coords.add(new Coord(t, x, y));
}
The problem will occur if you have an extra space at the end, because you are testing for counter < info.length and using counter + 1 and counter + 2. Try changing the loop conditions to:
for (int counter = 0; counter + 2 < info.length; counter = counter+3)
There is no need for external libraries.
You could just call dataHolder.trim(); which will remove any whitespace from the beginning and end your string. Then using dataHolder.split("\s"); //splits on "whitespace", you will receive an array consisting only of your data and with the appropriate size.
This will save you all the checks at each iteration whether counter+2 is still within the scope of the array. While still a valid solution, this could introduce further problems in the future due to its inherent nature of being "check-to-validate" - you simply might forget to process one of the cases - while trimming the string beforehand makes it structurally, constructed valid and there is no need to process special cases.

Categories