How to improve the performance of this code, reducing the compile time and keeping the functionality of the code same ?
The code is to extract two sub-strings from different strings and concatinating them to provide the largest possible palindromic string.
the Question was :You have two strings, (a) and (b). Find a string, (c), such that: (c)=(d)+(e).
(d),(e) can be expressed as where (d) is a non-empty substring of (a) and (e) is a non-empty substring of (b).
(c) is a palindromic string.
The length of is as long as possible.
For each of the pairs of strings (a) and (b) received as input, find and print string on a new line. If you're able to form more than one valid string , print whichever one comes first alphabetically. If there is no valid answer, print -1 instead.
import java.io.*;
import java.util.*;
public class Solution {
boolean isPalindrome(String s) {
int n = s.length();
for (int i=0;i<(n / 2);++i) {
if (s.charAt(i) != s.charAt(n - i - 1)) {
return false;
}
}
return true;
}
public static void main(String[] args) {
String result="";
Scanner in = new Scanner(System.in);
int n = in.nextInt();
for(int a=0; a<n; a++)
{int length1, length2, i,c,d,j;
int max_length=0;
String string1 = in.next();
String sub1,sub2;
String string2 = in.next();
length2=string2.length();
length1 = string1.length();
for( c = 0 ; c <length1 ; c++ )
{
for( i = length1-c ; i >0 ; i-- )
{
sub1 = string1.substring(c, c+i);
for( d = 0 ; d < length2 ; d++ )
{
for( j = length2-d ; j >0 ; j-- )
{
sub2 = string2.substring(d, d+j);
String temp=sub1+sub2;
Solution obj= new Solution();
if(temp.length()>=max_length && obj.isPalindrome(temp)==true)
{
if (max_length==temp.length())
{ if(temp.compareTo(result)<0)
{
result=temp;
}}
else {
max_length=temp.length();
result=temp;
}
}
}
}
}
}
if(max_length==0)
System.out.println(-1);
else
{
System.out.println(result);
result="";
}
} /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
}
}
I assume you want to reduce execution time, as opposed to compile time.
It is always best to avoid guessing, and rather determine exactly how the time is spent.
This is a good example.
If you do have a guess, this will prove it, or disprove it by showing you what the real problem is.
I have a guess (and it's only a guess).
You have a three-level nested loop, and inside the innermost loop I see a bunch of things that look suspicious.
The biggest one is new Solution().
That hits the memory manager, which can be very costly, not only to make the objects, but to clean them up.
Maybe you could move that out of the inner loop.
After that comes String temp=sub1+sub2;, which also hits the memory manager to make a new string.
Maybe you could make use the the string builder.
After that comes isPalindrome.
Whether that is efficient or not, I don't know.
Finally, your code needs much more disciplined indentation.
That alone can cause all kinds of bugs due to not being able to follow what you're doing.
Related
Below Java code produces the valid output but it takes more time to execute. Code works fine in eclipse, but it do not work in an online compiler like hackerrank or hackerearth since it takes more time for execution.Someone help me to find the solution for my time complexity problem.
I have tried to find the solution of the problem but i wasn't able to fix the performance by reducing the time..
Scanner scan = new Scanner(System. in );
String s = "aab";
String s1 = "";
String s2 = "";
int n1 = 0;
int length = 0;
long n = 882787;
long count = 0;
while (s1.length() < n) {
s1 = s1 + s;
}
if (s1.length() > n) {
count = s1.length() - n;
n1 = (int) count;
}
for (int i = 0; i < s1.length() - n1; i++) {
if (s1.charAt(i) == 'a') {
length += 1;
}
}
System.out.println(length);
Explanation of the above program:
I have a string s,in lowercase English letters that .I have repeat the string for n times and I store it in the new string.
I have to find the number of occurrences of 'a' in my new string
How do i actually reduce the time complexity for the above program
Thanks in advance
I would use a regular expression to create a String based on the initial input consisting of only letter 'a'(s). Take the length of that String and multiply it by n. That is one line that looks like
System.out.println(s.replaceAll("[^a]+", "").length() * n);
You are going to add s to the string n/s.length() times, call this N:
int N = n / s.length();
Each time you add s to the string you are going to append the number of As in s:
int a = 0;
for (int i = 0; i < s.length(); ++i) {
a += s.charAt(i) == 'a' ? 1 : 0;
}
// Or int a = s.replaceAll("[^a]", "").length();
So multiple these together:
int length = a * N;
String is immutable. Modification of a string is in fact create a new String object and put both old and new String into Java String constant poom
If you don't want to change your algorithm, I'd suggest to use StringBuilder to improve the speed of the execution. Note that StringBuilder is not thread safe
String s="aab";
int n1 = 0;
StringBuilder sb1 = new StringBuilder();
int length=0;
long n=882787;
long count=0;
while(sb1.length() < n) {
sb1.append(s);
}
if(sb1.length()>n) {
count =sb1.length()-n;
n1=(int)count;
}
for(int i=0;i<sb1.length()- n1;i++) {
if(sb1.charAt(i)=='a') {
length+=1;
}
}
System.out.println(length);
From here
When to use which one :
If a string is going to remain constant throughout the program, then
use String class object because a String object is immutable. If a
string can change (example: lots of logic and operations in the
construction of the string) and will only be accessed from a single
thread, using a StringBuilder is good enough. If a string can change,
and will be accessed from multiple threads, use a StringBuffer because
StringBuffer is synchronous so you have thread-safety.
I see multiple possible optimizations:
a) One pattern that is not that good is creating lots of Strings through repeated string concatenation. Each "s1 = s1 + s;" creates a new instance of String which will be obsolet the next time the command runs (It increases the load, because the String instances will be additional work for the Garbage Collector).
b) Generally: If you find, that your algorithm takes too long, then you should think about a complete new way to solve the issue. So a different solution could be:
- You know the length you want to have (n) and the length of the small string (s1) that you use to create the big string. So you can calculate: How often will the small string be inside the target string? How many characters are left?
==> You can simply check the small string for the character you are looking for. That multiplied by the number how often the small string will be inside the big string is the first result that you get.
==> Now you need to check the substring of the small string that are missing.
Example: n=10, s1="aab", Looking for "a":
So first we check how often the s1 will fit into a new string of n Characters n/length(s1) => 3
So we check how often the "a" is inside "aab" -> 2
First result is 3*2 = 6
But we checked for 3*3 = 9 characters so far, but we want 10 characters. So we need to check n % length(s1) = 1 character of s1 and in this substring ("a"), wie have 1 a, so we have to add 1.
So the result is 7 which we got without building a big string (which is not required at all!)
Just check how many times the char occurs in the original and multiple it by n. Here's a simple way to do so without even using regex:
// take these as function input or w/e
String s = "aab";
String find = "a";
long n = 882787;
int count = s.length() - s.replaceAll(find, "").length();
System.out.println(count * n);
i get termination due to timeout error when i compile. Please help me
Given two strings, determine if they share a common substring. A substring may be as small as one character.
For example, the words "a", "and", "art" share the common substring "a" . The words "be" and "cat" do not share a substring.
Input Format
The first line contains a single integer , the number of test cases.
The following pairs of lines are as follows:
The first line contains string s1 .
The second line contains string s2 .
Output Format
For each pair of strings, return YES or NO.
my code in java
public static void main(String args[])
{
String s1,s2;
int n;
Scanner s= new Scanner(System.in);
n=s.nextInt();
while(n>0)
{
int flag = 0;
s1=s.next();
s2=s.next();
for(int i=0;i<s1.length();i++)
{
for(int j=i;j<s2.length();j++)
{
if(s1.charAt(i)==s2.charAt(j))
{
flag=1;
}
}
}
if(flag==1)
{
System.out.println("YES");
}
else
{
System.out.println("NO");
}
n--;
}
}
}
any tips?
Below is my approach to get through the same HackerRank challenge described above
static String twoStrings(String s1, String s2) {
String result="NO";
Set<Character> set1 = new HashSet<Character>();
for (char s : s1.toCharArray()){
set1.add(s);
}
for(int i=0;i<s2.length();i++){
if(set1.contains(s2.charAt(i))){
result = "YES";
break;
}
}
return result;
}
It passed all the Test cases without a time out issue.
The reason for the timeout is probably: to compare two strings that each are 1.000.000 characters long, your code needs 1.000.000 * 1.000.000 comparisons, always.
There is a faster algorithm that only needs 2 * 1.000.000 comparisons. You should use the faster algorithm instead. Its basic idea is:
for each character in s1: add the character to a set (this is the first million)
for each character in s2: test whether the set from step 1 contains the character, and if so, return "yes" immediately (this is the second million)
Java already provides a BitSet data type that does all you need. It is used like this:
BitSet seenInS1 = new BitSet();
seenInS1.set('x');
seenInS1.get('x');
Since you're worried about execution time, if they give you an expected range of characters (for example 'a' to 'z'), you can solve it very efficiently like this:
import java.util.Arrays;
import java.util.Scanner;
public class Whatever {
final static char HIGHEST_CHAR = 'z'; // Use Character.MAX_VALUE if unsure.
public static void main(final String[] args) {
final Scanner scanner = new Scanner(System.in);
final boolean[] characterSeen = new boolean[HIGHEST_CHAR + 1];
mainloop:
for (int word = Integer.parseInt(scanner.nextLine()); word > 0; word--) {
Arrays.fill(characterSeen, false);
final String word1 = scanner.nextLine();
for (int i = 0; i < word1.length(); i++) {
characterSeen[word1.charAt(i)] = true;
}
final String word2 = scanner.nextLine();
for (int i = 0; i < word2.length(); i++) {
if (characterSeen[word2.charAt(i)]) {
System.out.println("YES");
continue mainloop;
}
}
System.out.println("NO");
}
}
}
The code was tested to work with a few inputs.
This uses a fast array rather than slower sets, and it only creates one non-String object (other than the Scanner) for the entire run of the program. It also runs in O(n) time rather than O(n²) time.
The only thing faster than an array might be the BitSet Roland Illig mentioned.
If you wanted to go completely overboard, you could also potentially speed it up by:
skipping the creation of a Scanner and all those String objects by using System.in.read(buffer) directly with a reusable byte[] buffer
skipping the standard process of having to spend time checking for and properly handling negative numbers and invalid inputs on the first line by making your own very fast int parser that just assumes it's getting the digits of a valid nonnegative int followed by a newline
There are different approaches to solve this problem but solving this problem in linear time is a bit tricky.
Still, this problem can be solved in linear time. Just apply KMP algorithm in a trickier way.
Let's say you have 2 strings. Find the length of both strings first. Say length of string 1 is bigger than string 2. Make string 1 as your text and string 2 as your pattern. If the length of the string is n and length of the pattern is m then time complexity of the above problem would be O(m+n) which is way faster than O(n^2).
In this problem, you need to modify the KMP algorithm to get the desired result.
Just need to modify the KMP
public static void KMPsearch(char[] text,char[] pattern)
{
int[] cache = buildPrefix(pattern);
int i=0,j=0;
while(i<text.length && j<pattern.length)
{
if(text[i]==pattern[j])
{System.out.println("Yes");
return;}
else{
if(j>0)
j = cache[j-1];
else
i++;
}
}
System.out.println("No");
return;
}
Understanding Knuth-Morris-Pratt Algorithm
There are two concepts involved in solving this question.
-Understanding that a single character is a valid substring.
-Deducing that we only need to know that the two strings have a common substring — we don’t need to know what that substring is.
Thus, the key to solving this question is determining whether or not the two strings share a common character.
To do this, we create two sets, a and b, where each set contains the unique characters that appear in the string it’s named after.
Because sets 26 don’t store duplicate values, we know that the size of our sets will never exceed the letters of the English alphabet.
In addition, the small size of these sets makes finding the intersection very quick.
If the intersection of the two sets is empty, we print NO on a new line; if the intersection of the two sets is not empty, then we know that strings and share one or more common characters and we print YES on a new line.
In code, it may look something like this
import java.util.*;
public class Solution {
static Set<Character> a;
static Set<Character> b;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i = 0; i < n; i++) {
a = new HashSet<Character>();
b = new HashSet<Character>();
for(char c : scan.next().toCharArray()) {
a.add(c);
}
for(char c : scan.next().toCharArray()) {
b.add(c);
}
// store the set intersection in set 'a'
a.retainAll(b);
System.out.println( (a.isEmpty()) ? "NO" : "YES" );
}
scan.close();
}
}
public String twoStrings(String sOne, String sTwo) {
if (sOne.equals(sTwo)) {
return "YES";
}
Set<Character> charSetOne = new HashSet<Character>();
for (Character c : sOne.toCharArray())
charSetOne.add(c);
Set<Character> charSetTwo = new HashSet<Character>();
for (Character c : sTwo.toCharArray())
charSetTwo.add(c);
charSetOne.retainAll(charSetTwo);
if (charSetOne.size() > 0) {
return "YES";
}
return "NO";
}
This must work. Tested with some large inputs.
Python3
def twoStrings(s1, s2):
flag = False
for x in s1:
if x in s2:
flag = True
if flag == True:
return "YES"
else:
return "NO"
if __name__ == '__main__':
q = 2
text = [("hello","world"), ("hi","world")]
for q_itr in range(q):
s1 = text[q_itr][0]
s2 = text[q_itr][1]
result = twoStrings(s1, s2)
print(result)
static String twoStrings(String s1, String s2) {
for (Character ch : s1.toCharArray()) {
if (s2.indexOf(ch) > -1)
return "YES";
}
return "NO";
}
I was wondering if anyone could help me figuring out why my code doesn't do what I expect it to do. The idea was to count the same following letters in a StringBuffer and transform it into something like this AABBC => 2A2B1C. Now my program doesn't do that and it probably has to do with my poor usage of these newly-learned concepts. Do I have to convert marker into a char for it to print it out? Or is the structure of my code inherently wrong? I'm also not sure what I can do with StringBuffers and what not.
package package1;
public class Strings {
public static void main(String[]args){
int marker = 1;
StringBuffer s2 = new StringBuffer();
StringBuffer s = new StringBuffer("AAAA");
for(int i = 0; i<=s.length(); i++){
while(s.charAt(i) == s.charAt(i+1)){
marker++;
}
i += marker;
s2.append(marker);
s2.append(s.charAt(i));
marker = 0;
}
System.out.println(s2); // It simply prints out nothing
}
}
You have an off-by-one-error bug in your code.
You are counting how many times you've seen a char starting from 1 but string indexes start from zero and you are mixing up the two later when you are assigning marker to i using getCharAt(i) is off by one (in this case it tries to get the char at index 4 which is passed the end of the string).
An Easy way to fix it is to have your count (marker) start at 0 too and only increase by one what you are appending to the string:
package package1;
public class Strings {
public static void main(String[]args){
int marker = 0; // changing this to 0
StringBuffer s2 = new StringBuffer();
StringBuffer s = new StringBuffer("AAAA");
for(int i = 0; i<=s.length(); i++){
while(s.charAt(i) == s.charAt(i+1)){
marker++;
}
i += marker;
s2.append(marker + 1); // print out the count plus one because we are counting from zero
s2.append(s.charAt(i));
marker = 0;
}
System.out.println(s2); // It simply prints out nothing
}
}
Your while-loop never finishes.
If s.charAt(i) == s.charAt(i+1) is true, the marker gets increased. But because i stays the same the condition of your while-loop stays the same, so it runs for ever.
There are some more bugs in your code (like i <= s.length() and try to access s.charAt(i+1) will lead to IndexOutOfBoundsException) but you will find them.
So recently I got invited to this google foo.bar challenge and I believe the code runs the way it should be. To be precise what I need to find is the number of occurrences of "abc" in a String. When I verify my code with them, I pass 3/10 test cases. I'm starting to feel bad because I don't know what I am doing wrong. I have written the code which I will share with you guys. Also the string needs to be less than 200 characters. When I run this from their website, I pass 3 tests and fail 7. Basically 7 things need to be right.
The actual question:
Write a function called answer(s) that, given a non-empty string less
than 200 characters in length describing the sequence of M&Ms. returns the maximum number of equal parts that can be cut from the cake without leaving any leftovers.
Example : Input : (string) s = "abccbaabccba"
output : (int) 2
Input: (string) s = "abcabcabcabc"
output : (int) 4
public static int answer(String s) {
int counter = 0;
int index;
String findWord ="ABC";
if(s!=null && s.length()<200){
s = s.toUpperCase();
while (s.contains(findWord))
{
index = s.indexOf(findWord);
s = s.substring(index + findWord.length(), s.length());
counter++;
}
}
return counter;
}
I see a couple of things in your code snippet:
1.
if(s.length()<200){
Why are you checking for the length to be lesser than 200? Is that a requirement? If not, you can skip checking the length.
2.
String findWord ="abc";
...
s.contains(findWord)
Can the test program be checking for upper case alphabets? Example: "ABC"? If so, you might need to consider changing your logic for the s.contains() line.
Update:
You should also consider putting a null check for the input string. This will ensure that the test cases will not fail for null inputs.
The logic of your code is well but on the other hand i found that you didn't check for if input string is empty or null.
I belief that google foo.bar wants to see the logic and the way of coding in a proper manner.
so don't be feel bad
I would go for a simpler approach
int beforeLen = s.length ();
String after = s.replace (findWord, "");
int afterLen = after.length ();
return (beforeLen - afterLen) / findWord.length ();
String pattern = "abc";
String line="<input text here>";
int i=0;
Pattern TokenPattern=Pattern.compile(pattern);
if(line!=null){
Matcher m=TokenPattern.matcher(line);
while(m.find()){
i++;
}}
System.out.println("No of occurences : "+ " "+i);
put declaration of index out before while block, isn't never good re-declare the same variable n time.
int index;
while (s.contains(findWord))
{
index = s.indexOf(findWord);
....
}
I hope this help
Update:
try to compact your code
public static int answer(String s) {
int counter = 0;
int index;
String findWord = "ABC";
if (s != null && s.length() < 200) {
s = s.toUpperCase();
while ((index = s.indexOf(findWord)) > -1) {
s = s.substring(index + findWord.length(), s.length());
counter++;
}
}
return counter;
}
Update:
The logic seems good to me, I'm still try to improve the performance, if you can try this
while ((index = s.indexOf(findWord, index)) > -1) {
//s = s.substring(index + findWord.length(), s.length());
index+=findWord.length();
counter++;
}
I have a string of annotation of country abbreviation , I want to split them out so I can identify the country of each abbreviation. Such that I will have String c = USA; I will output the country name...
currently it doesnt have c = USA but only A
public class Example {
public static void main(String[] args) {
String x = "USAIND";
String c = "";
System.out.print("Country: ");
for (int i = 0; i < 3; i++) {
c = Character.toString(x.charAt(i));
System.out.print(c);
if (c.equals("USA")) {
System.out.println("United State of America");
}
}
System.out.println("");
System.out.print("Country: ");
for (int i = 3; i < 6; i++) {
c = Character.toString(x.charAt(i));
System.out.print(c);
if (c.equals("IND")) {
System.out.println("India");
}
}
System.out.println("");
}
}
You need to append each character to your String and then compare it, otherwise, it'll just keep replacing your String with the last character always.
for (int i = 0; i < 3; i++) {
c += Character.toString(x.charAt(i)); // Appending all the characters one by one
}
System.out.print(c); // Printing the String c after all the characters are appending
if (c.equals("USA")) { // checking if its equal to USA
System.out.println("United State of America");
}
And the same goes with the other half of the process.
c = ""; // re-initialize it to blank
for (int i = 3; i < 6; i++) {
c += Character.toString(x.charAt(i));
}
System.out.print(c);
if (c.equals("IND")) {
System.out.println("India");
}
But the easiest way would be to use String.substring(startIndex, endIndex) for this.
String c = x.substring(0,3); // For USA
String c1 = x.substring(3,6); // For IND
because when you do this
c = Character.toString(x.charAt(i));
the character at the ith position is getting stored in c and as it is in a for loop the only thing that would be stored there would be 'A'
use a substring instead of the for loop and charAt
c = x.substring(0,3); \\which would give you "USA"
You should probably use String.substring(...) for this.
You are iterating through the string, but you only retain the last character because of this snippet:
c = Character.toString(x.charAt(i));
This should be:
c += Character.toString(x.charAt(i));
As this will append the current character iteration to the overall string. Replace the snippets with this fix, for the two loops. The c variable will build up the country code and will pass this condition this time:
if (c.equals("USA")) {
After the first loop and before the second loop, you will need to re-initialize the c variable:
c = "";
Once done, you can put that logic in a method of its own, so you avoid duplicate code within the loops.
This logic could be simplified by using String.substring instead, as others pointed out, as you work in details with the String.charAt which is more tedious. I thought though that pointing out your logic error was worth it, before giving you other pointers.
So talking about other approaches, you could try another one to your country code and name console output. Try to use a HashMap where the keys are the country code and the value is the country's name. You can iterate through the HashMap after that and print out both keys and values. That would be more high-level to your current solution and way shorter in code.
EDIT1: I offered the code to the last suggestion but I removed it, as I realized that giving code to assignment related questions is not encouraged.
I would go another way, either try to add a terminal symbol like ";". If this is not possible you could check with String.contains("USA") if a certain country is set. But beaware it could happen that you will find a country not listed in because the combination of two others.
With your logic, I thing it will be better to use
public String substring(int beginIndex, int endIndex)
Get the substring for a start index and the end index and compare. Currently you are just getting the character .With your current implementation you have to convert each character to String and append.
For the part of splitting, I would use Guava's Splitter, like so:
Splitter.fixedLength(3).split(x)
And for the part of matching abbreviation with full name, I would use an enum instead of equals comparing, it seems a little cleaner. So, a possible full result would be:
public class Example {
public static enum Country {
USA("United States of America"), IND("India");
String fullName;
Country(String fullName) {
this.fullName = fullName;
}
String getFullName() {
return fullName;
}
};
public static void main(String[] args) {
String x = "USAINDUSAIND";
for (String s : Splitter.fixedLength(3).split(x)) {
System.out.println(Country.valueOf(s).getFullName());
}
}
}
EDIT: Sorry about using third party, seemed clean to me. I agree with the other answers in using substring, but I like more thinking of everything as just one process and not two, so you can easily have a string of more than 2 abbreviations, like (example assuming the Enum as above)
public static void main(String[] args) {
String x = "USAINDUSAIND";
for (int i = 0;i < x.length();i += 3) {
System.out.println(Country.valueOf(x.substring(i, i + 3).getFullName());
}
}