I have solved hackerrank Java Map Problem but I had timeout result for 2 cases. When I changed just the printf line problem solved. However I could not understand why it is ? Here my code:
import java.util.*;
import java.io.*;
class Solution{
private static HashMap<String, Integer> phoneBook = new HashMap<>();
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String search = "";
int n=in.nextInt();
in.nextLine();
for(int i=0; i<n; i++) {
String name=in.nextLine();
int phone=in.nextInt();
phoneBook.put(name, phone);
in.nextLine();
}
while(in.hasNext()) {
search = in.nextLine();
if (phoneBook.get(search) != null)
System.out.printf(search + "=" + phoneBook.get(search) + "\n"); // this works
// System.out.printf("%s=%d\n", search, phoneBook.get(search)); // this does not work why ?
else
System.out.println("Not found");
}
}
Formatter.format() takes time
Formatter.format() (and its shortcut System.out.printf()) requires parsing, validating and formatting. All that takes a huge amount of time.
So just dump the data as quick as possible. Hackerrank requires you to make fast programs, so just do that: a fast program. Concatenate your items instead of formatting them. Also, if you have no parameter, just use System.out.println() instead of System.out.printf.
When you do a formatting output, java parses the string to format ("%s=%d\n" in your case) to put values instead placeholders. It takes at least O(n) complexity, which is important in your case.
Also you call phoneBook.get(search) twice. Try to keep the result in a variable. It also can speed up the program.
To break the string into separate lines, we need to use %n specifier in printf statement.
System.out.printf("%s=%d%n", search, phoneBook.get(search));
OR you can use the String.format() shown in the below statement.
System.out.println(String.format("%s=%d",s,p));
Short Answer: Lookup performance of Hashmap + Formatting the fetched value from the HashMap was computationally more Intensive than just concatenating the string without any format.
To be a bit verbose, the HashMap Implementation which is designed for an amortized complexity of O(1) for get(Key) functions does have a nasty complexity of O(n) (stressing the term worst case :) ) if either all or most of the keys share a common hash. Such occurrences, although not very common would've occurred when the test case feeder gave an enormous test case input with multiple duplicate values. Such a situation combined with simultaneous formatting would've resulted in a crash.
Related
I am trying to get factorial of given integer with the below code, but the final \b is not printing. Why?
import java.util.*;
class Example {
public static void main(String args[]) {
Scanner input = new Scanner(System.in);
System.out.println("INPUT AN INTEGER");
int num = input.nextInt();
System.out.print(num + "!=");
for(int i = num; i > 0; i = i - 1){
System.out.print(i + "*");
}
System.out.print("\b");
}
}
As indicated, the functionality of various output terminals varies wildly. There are full fledged VT100 terminals, Windows command line prompts, views within IDE's etc. So backspace is likely not supported by every one of them.
However, you should not generate text unless it is required. Do not backtrace or - in this case - backspace after the fact. That's an error prone process, and you use more memory resources by first creating the string in the first place. The latter is not a problem for a single *, but in general you try and avoid spurious memory use.
The following code should work:
for (int i = num; i > 0; i--){
if (i != num) {
print("*");
}
System.out.print(i);
}
Also notice the use of i-- instead of i = i - 1.
When you're ready to advance, you may want to have a look at String.format() and the StringBuilder class and create the full string before printing it out.
In principle you can of course use a substring to remove the final * character if you first create a string. Or you could remove it from the StringBuilder instance if you use that.
I would still prefer not appending it in the first place and use above code or a slight modification of it to build the entire string.
I got a "Terminated due to timeout error" when i ran my code for some specific testcases only. Even though my code compiled successfully for other testcases. Can someone please help me with this?
Link - https://www.hackerrank.com/challenges/phone-book
Problem Statement :
You are given a phone book that consists of people's names and their phone number. After that you will be given some person's name as query. For each query, print the phone number of that person.
Input Format :
The first line will have an integer denoting the number of entries in the phone book. Each entry consists of two lines: a name and the corresponding phone number.
After these, there will be some queries. Each query will contain a person's name. Read the queries until end-of-file.
Constraints:
1<=n<=100000
1<=Query<=100000
A person's name consists of only lower-case English letters and it may be in the format 'first-name last-name' or in the format 'first-name'. Each phone number has exactly 8 digits without any leading zeros.
Output Format :
For each case, print "Not found" if the person has no entry in the phone book. Otherwise, print the person's name and phone number. See sample output for the exact format.
To make the problem easier, we provided a portion of the code in the editor. You can either complete that code or write completely on your own.
My code is as follows :
import java.util.*;
import java.io.*;
class Solution
{
public static void main(String []args)
{
Scanner in = new Scanner(System.in);
int n=in.nextInt();
in.nextLine();
ArrayList<String> name = new ArrayList<String>();
int[] phone = new int[100000];
for(int i=0;i<n;i++)
{
name.add(in.nextLine());
phone[i]=in.nextInt();
in.nextLine();
}
while(in.hasNext())
{
String s=in.nextLine();
int a=name.indexOf(s);
if(a>=0)
{
System.out.println(s + "=" + phone[a] );
}
else
{
System.out.println("Not found");
}
}
}
}
PS:This is my first question on the forum. I'm an amateur learning java. Sorry if i violated any of the many rules of asking a question :( . Please do correct me and help me contribute to the community here in a good way :)
The problem with your logic is that it is implemented using ArrayList which is a sequential structure. Any search in List will be sequential and for large test cases its taking too much time to lookup in your names list.
Hash map is more appropriate for a phone book example as it keeps data in key, value pair and look ups are fast because of hashing
Here is a version that is implemented using HashMap
Map<String,Integer> phonebook = new HashMap<>();
Scanner in = new Scanner(System.in);
int n=in.nextInt();
in.nextLine();
for(int i=0;i<n;i++)
{
String name=in.nextLine();
int phone=in.nextInt();
in.nextLine();
phonebook.put(name,phone);
}
while(in.hasNext())
{
String s=in.nextLine();
Integer phone = phonebook.get(s);
if(phone==null){
System.out.println("Not found");
} else {
System.out.println(s+"="+phone);
}
}
Hope this explains.
Usually "Terminated due to timeout error" occurs when your code takes longer time to execute than the maximum time set by the Problem Setters(Hackerrank).
The problem you tried is intended to teach you how HashMaps are used, but you solved the problem using arrays. Searching in arrays takes O(n)longer time than that of Maps which are generally hashed to search in O(1) time. For smaller input your program works fine, but for larger inputs like 100000 entries, It will take longer time and result in time out. So Use Maps instead of Arrays and ArrayLists
I am fairly new to Java. Over the past few weeks I have been trying to teach myself java. This has been primarily based on tutorials i find online and forums I can find. So keep this in mind and any additional critique you can share is greatly appreciated! I am currently trying to create a calculator that runs off of if-else loops. I'm working on a method that allows the user to derive a function based on the principle that if
f(x)=ax^n+bx^o+cx^p... then f'(x)=anx^n-1+box^o-1+cpx^p-1...
I'm trying to use .split() to separate the parts of the function, perform the changes to the individual parts, and then print them together. I could get most of the way through this but I couldn't convert a string with a negative sign to an integer so I am trying to call a method that uses .substring and then replaceAll to get rid of the negative sign then convert to integer. However, I keep getting a compiling error stating the "actual and formal argument lists differ in length". Can anyone explain why this might be happening?
import java.util.Scanner;
import java.util.Arrays;
import java.lang.String;
public class InputInteger
{
public String changeSign(String second) {
String negative = second.substring(0,1);
return negative;
}
public static void splitFunction() {
Scanner o = new Scanner(System.in);
String function = o.next();
String[] parts = function.split("(?=\\+|\\-)");
for (int i = 0; i < parts.length;) {
String[] second = parts[i].split("(?=[0-9]+|[a-z]+|[A-Z]+\\^)");
InputInteger.changeSign();
if (negative = ("-")) {
second = second.replace("-","");
int x = Integer.parseInt(second[0]);
int y = Integer.parseInt(second[2]);
int w = x*y;
int z = y-1;
System.out.println(w + "x^" + z);
i++;
}
}
}
Problem that you are talking about is the method not working . You have to pass argument in the function like
InputInteger.changeSign(function);
or
InputInteger.changeSign(second[i]);
according to requirement
changeSign(String second) should be defined as static
negative variable is not defined
you should compare strings with equals() method
you call .replace(...) on an array, which doesn't have this method
And these are only compile errors, I see at least one runtime problem:
you increase i only in the if which may result in an infinite
loop...
I suggest you use some good IDE like Eclipse or IntelliJ IDEA, which will help you with warnings/errors/etc.
First of all in your code if (negative = ("-")) you have a single "=" and I think you meant to use "==" for comparison. Second, method parseInt() as well as valueOf() (which I prefer to parseInt()) should handle negative numbers just fine. there is no need to remove "-". Yout method changeSign() takes a String argument, also your method ChangeSign() returns String value and you must assign a result to some String: String negative = InputInteger.changeSign(str);. Plus also String class has a method startsWith(String prefix) that fits your better then substring(). Hope this helps. If anything there is an Open source library that provides a Utility for parsing String into Integer (among other things). in That util there is a method
parseStringToInt(String numStr, int defaultValue,
String nullOrEmptyStringErrorMessage, String numberFormatErrorMessage)
That tries to parse a String to Integer and if it does not succeed it returns a default value and never throws an Exception. That method definitely works fine with negative integers. Here is the link to the article about the library.https://www.linkedin.com/pulse/open-source-java-library-some-useful-utilities-michael-gantman?trk=pulse_spock-articles. There you will find the instructions on where to get the library including javadoc and source code.
Im doing a frequency dictionary, in which i read 1000 files, each one with about 1000 lines. The approach i'm following is:
BufferedReader to read fileByFile
read the first file, get the first sentence, split the sentence to an array string, then fill in an hashmap with the values from the string array.
do this for all the senteces in that file
do this for all 1000 files
My problem is, this is not a very efficient way to do it, i'm taking about 4 minutes to do all this. I'v increased heap size, refactored the code to make sure i'm not doind something wrong. For this approach, i'm completly sure there's nothing i can improve in the code.
My bet is, each time a sentece is read, a split is applied, which, multiplied by 1000 sentences in a file and by 1000 files is a huge ammount of splits to process.
My idea is, instead of read and process file-by-file, i could read each file to a char array, and then make the split only once per file. That would ease the ammount of processing times consuming with the split. Any suggestions of implementation would be appreciated.
OK, I have just implemented the POC of your dictionary. Fast and dirty. My files contained 868 lines each one but I created 1024 copies of the same file. (This is table of contents of Spring Framework documentation.)
I ran my test and it took 14020 ms (14 seconds!). BTW I ran it from eclipse that could decrease the speed a little bit.
So, I do not know where your problem is. Please try my code on your machine and if it runs faster try to compare it with your code and understand where the root problem.
Anyway my code is not the fastest I can write.
I can create Pattern before loop and the use it instead of String.split(). String.split() calls Pattern.compile() every time. Creating pattern is very expensive.
Here is the code:
public static void main(String[] args) throws IOException {
Map<String, Integer> words = new HashMap<String, Integer>();
long before = System.currentTimeMillis();
File dir = new File("c:/temp/files");
for (File file : dir.listFiles()) {
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
String[] lineWords = line.split("\\s+");
for (String word : lineWords) {
int count = 1;
Integer currentCount = words.get(word);
if (currentCount != null) {
count = currentCount + 1;
}
words.put(word, count);
}
}
}
long after = System.currentTimeMillis();
System.out.println("run took " + (after - before) + " ms");
System.out.println(words);
}
If you dont care about the the contents are in different files I would do the approach your are recommending. Read all files and all lines into memory (string, or char array, whatever) and then do the 1 split and hash populate based on the one string/dataset.
If I understand what you're doing, I don't think you want to use strings except when you access your map.
You want to:
loop through files
read each file into a buffer of something like 1024
process the buffer looking for word end characters
create a String from the character array
check your map
if found, update your count, if not, create a new entry
when you reach end of buffer, get the next buffer from the file
at end, loop to next file
Split is probably pretty expensive since it has to interpret the expression each time.
Reading the file as one big string and and then splitting that sounds like a good idea. String splitting/modifying can be surprisingly 'heavy' when it comes to garbage collection. Multiple lines/sentences means multiple Strings and with all the splits it means a huge amount of Strings (Strings are immutable, so any change to them will actually create a new String or multiple Strings)... this produces a lot of garbage to be collected, and the garbage collection could become a bottleneck (with a smaller heap, the maximum amount of memory is reached all the time, kicking off a garbage collection, which potentially needs to clean up hundreds of thousands or millions of separate String-objects).
Of course, without knowing your code this is just a wild guess, but back in the day, I got an old command line Java-programs' (it was a graph-algorithm producing a huge SVG-file) running time to drop from about 18 seconds to less than 0.5 seconds just by modifying the string-handling to use StringBuffers/Builders.
Another thing that springs to mind is using multiple threads (or a threadpool) to handle different files concurrently, and then combine the results at the end. Once you get the program to run "as fast as possible", the remaining bottleneck will be the disk access, and the only way (afaik) to get past that is faster disks (SSDs etc.).
Since you're using a bufferedReader, why do you need to read in a whole file explicitly? I definitely wouldn't use split if you're after speed, remember, it has to evaluate a regular expression each time you run it.
Try something like this for your inner loop (note, I have not compiled this or tried to run it):
StringBuilder sb = null;
String delimiters = " .,\t"; //Build out all your word delimiters in a string here
for(int nextChar = br.read(); nextChar >= 0; nextChar = br.read()) {
if(delimiters.indexOf(nextChar) < 0) {
if(sb == null) sb = new StringBuilder();
sb.append((char)(nextChar));
} else {
if(sb != null) {
//Add sb.toString() to your map or increment it
sb = null;
}
}
}
You could try using different sized buffers explicitly, but you probably won't get a performance improvement over this.
One very simple approach which uses minimum heap space and should be (almost) as fast as anything else would be like
int c;
final String SEPARATORS = " \t,.\n"; // extend as needed
final StringBuilder word = new StringBuilder();
while( ( c = fileInputStream.read() ) >= 0 ) {
final char letter = (char) c;
if ( SEPARATORS.indexOf(letter) < 0 ) {
word.append(letter);
} else {
processWord( word.toString() );
word.setLength( 0 );
}
}
extend for more separator characters as needed, possibly use multi-threading to process multiple files concurrently until disc IO becomes the bottle neck...
My assignment is to ask a user for the number of strings he would like to input. Then i will prompt him to right the strings. Then i am supposed to print the stings in alphabetical order. I got most of the assignment done, just need a sorting algorithm such as Bubble sort to finish it. this is my code.
import java.io.*;
public class sorting
{
private static BufferedReader stdin=new BufferedReader(new InputStreamReader( System.in));
public static void main(String[] arguments) throws IOException
{
System.out.println("How many strings would you like to enter?");
int stringCount = Integer.parseInt(stdin.readLine());
String[] stringInput = new String[stringCount];
for(int i = 0; i < stringCount; i++)
{
System.out.print("Could you enter the strings here: ");
stringInput[i] = stdin.readLine();
}
//Now how do i use a sorting algorithm?
}
}
Arrays.sort().
Use Rhino as a Javascript parser so you can include jQuery into your project. Then sorting becomes trivial as you can just load the Strings into a <table> and run this nifty plugin on it.
^ DO NOT DO THIS. (well if you do, post the source and tell us what grade you got on the assignment ;)
No really, just write bubble sort by yourself. It's not that long. You probably learned the pseudocode in class. If you need an additional reference, take a look at the Wikipedia article on it. If there's something in particular that you don't understand about the algorithm, post a specific question and we'll help you out. Other than that, you look like you're on the right track so far :)
If this wasn't an exercise you'd just use Arrays.sort(stringInput)
import java.io.*;
import java.util.*;
public class sorting
{
public static Scanner input = new Scanner(System.in);
public static void main(String[] args) throws IOException
{
System.out.print("How many strings would you like to enter? ");
String[] stringInput = new String[Integer.parseInt(input.nextLine())];
for(int i = 0; i < stringInput.length; i++)
{
System.out.print("Could you enter the strings here: ");
stringInput[i] = input.nextLine();
}
Arrays.sort(stringInput);
for(String s : stringInput) System.out.println(s);
}
}
It seems to me that you are not being asked to sort the string array, but to print the strings in alphabetical order, which is likely to be a whole lot uglier but a whole lot simpler.
Once the user has typed all of the input strings, you might choose to iterate over the array stringCount times finding, in each iteration, the lowest-valued string; printing it; and then clearing it so that you won't see it in the next iteration.
BUT if you really are being asked to apply a bubble sort (or any kind of sort) on the array, well, that's a whole 'nother question, namely: how do I write a bubble sort for an array of strings. That's a tricky problem for anyone because the strings in the array are of differing lengths: they cannot just be swapped blindly but have to be written into some temp array somewhere that somehow knows how to accomodate their various lengths.
EDIT: Oh, wait a sec: maybe Java knows all about variable-length strings and how to handle them. If so, I take it all back.
Just use the Set str = TreeSet();
then loop through the string str.add("z");
when you iterate the str variable it is automatically sorted.