I'm currently working through Cracking the Coding Interview and I'm looking for some advice on what I can do to correct this algorithm. It appears to be working with some test cases, but it does not work with the test case ['a','a','a','b','b','b] as input. Any ideas what I'm doing wrong? Thanks!
Result expected = ['a','b']
Actual result = ['a']
/**
* Removes duplicate chars
*
* #param str
*/
public static void removeDuplicates(char[] str) {
if (str.length < 2) {
return;
}
for (int i = 0; i < str.length; i++) {
for (int j = 0; j < str.length; j++) {
if ((str[i] == str[j]) && (i != j)) {
str[j] = 0;
}
}
}
}
Try doing this:
public static void removeDuplicates(char[] str) {
if (str.length < 2) {
return;
}
for (int i = 0; i < str.length; i++) {
for (int j = 0; j < str.length; j++) {
System.out.println(i + "-" + j + " = " + str[j]); //added this line
if ((str[i] == str[j]) && (i != j)) {
str[j] = 0;
}
}
}
}
Why am I showing you this? This will show you what the removal process looks like as it goes along, and will help you better understand the problem. It actually works correctly.
I don't know how you got a result, because there is no print statement, and no return statement. But I did find a way to do it without using another char array(or any array for that matter). It simply reconstructs str. Check it out:
public static void main(String[] args) {
char[] chr = {'a','a','b','c','b','a','b','c'};
System.out.println(removeDuplicates(chr));
}
public static char[] removeDuplicates(char[] str) {
if (str.length < 2) {
return null;
}
for (int i = 0; i < str.length; i++) {
for (int j = 0; j < str.length; j++) {
if ((str[i] == str[j]) && (i != j)) {
str[j] = 0;
}
if (i == (str.length-1)) {
str[i] = str[j];
}
}
}
return str;
}
This example gives the output:
abc
Consider the next (pseudo) code:
if (str.length < 2) {
return;
}
good = 1; //current number of unique items
for (int i = 1; i < str.length; i++) {
success = 1;
//scan only unique items
for (int j = 0; j < good; j++) {
if ((str[i] == str[j]) {
success = 0;
break;
}
}
//new unique - copy at the final place
if (success) {
s[good] = str[i];
good++;
}
}
if (good<length)
str[good] = 0;
Related
I am working on implementing the longest palindromic substring problem and I followed the approach with DP and extra O(N^2) (yes I know there is an even more efficient algorithm but I am not interested in that in this post).
My implementation which basically uses the recurrence:
P(i, j) = P(i + 1, j - 1) ^ s[i] == s[j]
builds the relevant table but the run time is much slower than expected.
It does give the correct output if I run it in my IDE after several seconds (15+) but it is rejected by any online judge as too slow. I am not sure where the issue is since I am using memorization. So there is not recomputation of the same cases.
The strings that are starting to show that the algorithm has a performance issue are over 900 chars long.
Update
I am updating the question to add full source code and test case
Dynamic Programming approach O(N^2) time and O(N^2) space (not accepted and too slow)
public static String longestPalindromeDP(String s) {
Map<List<Integer>, Boolean> cache = new HashMap<>();
for(int i = 0; i < s.length(); i++) {
for(int j = 0; j < s.length(); j++) {
populateTable(s, i, j, cache);
}
}
int start = 0;
int end = 0;
for(int i = 0; i < s.length(); i++) {
for(int j = 0; j < s.length(); j++) {
if(cache.get(Arrays.asList(i, j))) {
if(Math.abs(start - end) < Math.abs(i - j)) {
start = i;
end = j;
}
}
}
}
return s.substring(start, end + 1);
}
private static boolean populateTable(String s, int i, int j, Map<List<Integer>, Boolean> cache) {
if(i == j) {
cache.put(Arrays.asList(i, j), true);
return true;
}
if(Math.abs(i - j) == 1) {
cache.put(Arrays.asList(i, j), s.charAt(i) == s.charAt(j));
return s.charAt(i) == s.charAt(j);
}
if(cache.containsKey(Arrays.asList(i, j))) {
return cache.get(Arrays.asList(i, j));
}
boolean res = populateTable(s, i + 1, j - 1, cache) && s.charAt(i) == s.charAt(j);
cache.put(Arrays.asList(i, j), res);
cache.put(Arrays.asList(j, i), res);
return res;
}
This is very slow in the populateTable but once it finishes the result is correct.
Brute force O(N^3) time and O(1) space: much faster and accepted
public static String longestPalindromeBruteForce(String s) {
if(s.length() == 1) {
return s;
}
String result = "";
for(int i = 0; i < s.length(); i++) {
for(int j = i + 1; j <= s.length(); j++) {
String tmp = s.substring(i, j);
if(isPalindrome(tmp)) {
if(tmp.length() > result.length()) {
result = tmp;
if(result.length() == s.length()) {
return result;
}
}
}
}
}
return result;
}
private static boolean isPalindrome(String s) {
for(int i = 0, j = s.length() - 1; i < j; i++, j--) {
if(s.charAt(i) != s.charAt(j)) {
return false;
}
}
return true;
}
Testing and input:
public static void main(String[] args) {
final String string1 = "civilwartestingwhetherthatnaptionoranynartionsoconceivedandsodedicatedcanlongendureWeareqmetonagreatbattlefiemldoftzhatwarWehavecometodedicpateaportionofthatfieldasafinalrestingplaceforthosewhoheregavetheirlivesthatthatnationmightliveItisaltogetherfangandproperthatweshoulddothisButinalargersensewecannotdedicatewecannotconsecratewecannothallowthisgroundThebravelmenlivinganddeadwhostruggledherehaveconsecrateditfaraboveourpoorponwertoaddordetractTgheworldadswfilllittlenotlenorlongrememberwhatwesayherebutitcanneverforgetwhattheydidhereItisforusthelivingrathertobededicatedheretotheulnfinishedworkwhichtheywhofoughtherehavethusfarsonoblyadvancedItisratherforustobeherededicatedtothegreattdafskremainingbeforeusthatfromthesehonoreddeadwetakeincreaseddevotiontothatcauseforwhichtheygavethelastpfullmeasureofdevotionthatweherehighlyresolvethatthesedeadshallnothavediedinvainthatthisnationunsderGodshallhaveanewbirthoffreedomandthatgovernmentofthepeoplebythepeopleforthepeopleshallnotperishfromtheearth";
//final String string2 = "ibvjkmpyzsifuxcabqqpahjdeuzaybqsrsmbfplxycsafogotliyvhxjtkrbzqxlyfwujzhkdafhebvsdhkkdbhlhmaoxmbkqiwiusngkbdhlvxdyvnjrzvxmukvdfobzlmvnbnilnsyrgoygfdzjlymhprcpxsnxpcafctikxxybcusgjwmfklkffehbvlhvxfiddznwumxosomfbgxoruoqrhezgsgidgcfzbtdftjxeahriirqgxbhicoxavquhbkaomrroghdnfkknyigsluqebaqrtcwgmlnvmxoagisdmsokeznjsnwpxygjjptvyjjkbmkxvlivinmpnpxgmmorkasebngirckqcawgevljplkkgextudqaodwqmfljljhrujoerycoojwwgtklypicgkyaboqjfivbeqdlonxeidgxsyzugkntoevwfuxovazcyayvwbcqswzhytlmtmrtwpikgacnpkbwgfmpavzyjoxughwhvlsxsgttbcyrlkaarngeoaldsdtjncivhcfsaohmdhgbwkuemcembmlwbwquxfaiukoqvzmgoeppieztdacvwngbkcxknbytvztodbfnjhbtwpjlzuajnlzfmmujhcggpdcwdquutdiubgcvnxvgspmfumeqrofewynizvynavjzkbpkuxxvkjujectdyfwygnfsukvzflcuxxzvxzravzznpxttduajhbsyiywpqunnarabcroljwcbdydagachbobkcvudkoddldaucwruobfylfhyvjuynjrosxczgjwudpxaqwnboxgxybnngxxhibesiaxkicinikzzmonftqkcudlzfzutplbycejmkpxcygsafzkgudy";
long startTime = System.nanoTime();
String palindromic = longestPalindromeDP(string1);
long elapsed = TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
System.out.println(elapsed);
System.out.println(palindromic);
}
The BruteForce finishes in 0 seconds.
The DynamicProgramming finishes in up to 9 seconds (depending on the machine)
What is the problem here?
I understand that there can be some optimization to improve the performance but how is it possible that the O(N^3) outperforms the O(N^2) since I use memoization?
Update
Update based on the answer of #CahidEnesKeleş
I replaced the List<Integer> as key with a custom object:
class IdxPair {
int i;
int j;
IdxPair(int i, int j) {
this.i = i;
this.j = j;
}
#Override
public boolean equals(Object o) {
if(o == null || !(o instanceof IdxPair)) return false;
if(this == o ) return true;
IdxPair other = (IdxPair) o;
return this.i == other.i && this.j == other.j;
}
#Override
public int hashCode() {
int h = 31;
h = 31 * i + 37;
h = (37 * h) + j;
return h;
}
}
Although a couple of test cases that previously failed, now pass it is still too slow overall and rejected by online judges.
I tried using c-like arrays instead of HashMap, here is the code:
public static String longestPalindromeDP(String s) {
int[][] cache = new int[s.length()][s.length()];
for (int i = 0; i < s.length(); i++) {
for (int j = 0; j < s.length(); j++) {
cache[i][j] = -1;
}
}
for(int i = 0; i < s.length(); i++) {
for(int j = 0; j < s.length(); j++) {
populateTable(s, i, j, cache);
}
}
int start = 0;
int end = 0;
for(int i = 0; i < s.length(); i++) {
for(int j = 0; j < s.length(); j++) {
if(cache[i][j] == 1) {
if(Math.abs(start - end) < Math.abs(i - j)) {
start = i;
end = j;
}
}
}
}
return s.substring(start, end + 1);
}
private static boolean populateTable(String s, int i, int j, int[][] cache) {
if(i == j) {
cache[i][j] = 1;
return true;
}
if(Math.abs(i - j) == 1) {
cache[i][j] = s.charAt(i) == s.charAt(j) ? 1 : 0;
return s.charAt(i) == s.charAt(j);
}
if (cache[i][j] != -1) {
return cache[i][j] == 1;
}
boolean res = populateTable(s, i + 1, j - 1, cache) && s.charAt(i) == s.charAt(j);
cache[i][j] = res ? 1 : 0;
cache[j][i] = res ? 1 : 0;
return res;
}
This code works faster than brute force approach. In my computer old dp finishes in ~5000 milliseconds, new dp finishes ~30 milliseconds, and bruteforce finishes in ~100 milliseconds.
Now that we know the reason for slowness, I conducted further experiments and measured the following codes' running time.
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
cache.put(Arrays.asList(i, j), true);
}
}
This code finishes in 2000 milliseconds. I further divided the expression to find exactly what is the source of slowness.
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
Arrays.asList(i, j);
}
}
This code finishes in 37 milliseconds.
Map<Integer, Boolean> cache = new HashMap<>();
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
cache.put(i*1000 + j, true);
}
}
This code finishes in 97 milliseconds.
Not Arrays.asList neither Map.put is slow. Maybe the hash function of the list is slow
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
Arrays.asList(i, j).hashCode();
}
}
This code finishes in 101 milliseconds.
No. This is fast as well. So maybe hash values collide most of the time. To test this, I put all hash codes inside a set and checked its size.
Set<Integer> hashSet = new HashSet<>();
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
hashSet.add(Arrays.asList(i, j).hashCode());
}
}
System.out.println(hashSet.size());
And it gave 31969. 31969 out of 1000000 is about %3,2. I think this is the source of the slowness. 1m item is too much for HashMap. It starts to move away from O(1) as more and more collisions occur.
I have a 2D array (a matrix of 10x10) with values ranging from 0 to -5.
I want a method to be triggered when there is a sequence of a value found within the array.
For example, there is a sequence of two negative 2. I want it to trigger an event/method that will give a bonus score of 4. This should happen only when there are two -2's and not if there is just one -2.
I tried achieving something like that but I cant figure out how to tell the program to only trigger when 'n' number of a value is found within the matrix.
public class Test {
static int board[][] = new int[10][10];
public static void Test() {
int i, j;
board[0][0] = -1;
board[0][1] = -1;
board[1][1] = -2;
board[1][2] = -2;
board[1][3] = -2;
board[1][4] = -2;
for (i = 0; i < board.length; i++) {
System.out.println("");
for (j = 0; j < board.length; j++) {
//board[i][j] = 0;
System.out.print(board[i][j]);
}
}
System.out.println();
}
public static void scanBoard() {
int i, j;
for (i = 0; i < board.length; i++) {
for (j = 0; j < board.length; j++) {
if (board[i][j] == -1) {
System.out.println("Hello");
}
}
}
}
public static void main(String[] args) {
Test(); //prints out whole array
scanBoard(); //scans for
}
}
public class Main {
static final int size = 10;
static int[][] matrix = new int[size][size];
public static void main(String[] args) {
System.out.println("The first matrix.\n");
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i == 3 && j > 3) {
matrix[i][j] = -2; //-2
} else {
matrix[i][j] = 1;
}
System.out.print(matrix[i][j]);
}
System.out.println();
}
scanBoard();
System.out.println("\nThe second matrix.\n");
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i == 9 && j > 5) {
matrix[i][j] = 2; //changed it from -2 to 2
} else {
matrix[i][j] = 1;
}
System.out.print(matrix[i][j]);
}
System.out.println();
}
scanBoard();
}
static void scanBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (matrix[i][j] == -2 && (j + 3 < size)) {
if (matrix[i][j + 1] == -2 && matrix[i][j + 2] == -2 && matrix[i][j + 3] == -2) {
System.out.println("\nThere you go, a special effect!".toUpperCase());
}
}
}
}
}
}
I am not sure if this is the result you wished to see according to your request. I hope this helps you. And I did some changes in your code so it will be easier to read (In my opinion lol).
public class Main {
static final int size = 10;
static int[][] matrix = new int[size][size];
public static void main(String[] args) {
System.out.println("The first matrix.\n");
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i == 9 && (j == 0 || j == 1)) {
matrix[i][j] = -2; //-2
} else {
matrix[i][j] = 1;
}
System.out.print(matrix[i][j]);
}
System.out.println();
}
scanBoard();
System.out.println("\nThe second matrix.\n");
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i == 8 && (j == 5 || j == 6)) {
matrix[i][j] = 2; //changed it from -2 to 2
} else {
matrix[i][j] = 1;
}
System.out.print(matrix[i][j]);
}
System.out.println();
}
scanBoard();
}
static void scanBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (matrix[i][j] == -2 && (j + 1 < size)) {
if (matrix[i][j + 1] == -2) {
//You can remove the '.toUpperCase()', it's just my personal preference
System.out.println("\nThere you go, a special effect!".toUpperCase());
}
}
}
}
}
}
From what I understood from the problem statement and comments, you want your scanBoard to behave like this:
public static void scanBoard(int value, int frequency) {
int i, j;
if (value <= 0 && value >= -5 && frequency >= 2 && frequency <= 10) {
for (i = 0; i < board.length; i++) {
int rowFrequency = 0;
for (j = 1; j < board.length; j++) {
if (board[i][j] == value && board[i][j - 1] == value) {
rowFrequency++;
} else {
rowFrequency = 0;
}
if (rowFrequency + 1 >= frequency) {
System.out.println("Hello");
}
}
}
}
}
public static void main(String[] args) {
Test(); //prints out whole array
scanBoard(-2, 4); //prints Hello once
scanBoard(-2, 3); //prints Hello twice
scanBoard(-2, 3); //prints Hello thrice
}
import java.util.Scanner;
public class Note {
public static void main(String[] args) {
String etudiants[][] = new String[1][4];
Scanner saisie = new Scanner(System.in);
for (int i = 0; i < 1; i++) {
System.out.print("\n\nEtudiant BTI00" + (i + 1) + "\n\n");
for (int j = 0; j < 4; j++) {
if (j == 0) {
System.out.print("\n\tCode de l'etudiant : ");
} else if (j == 1) {
System.out.print("\n\tNom etudiant : ");
} else if (j == 2) {
System.out.print("\n\tNote Maths : ");
} else if (j == 3) {
System.out.print("\n\tNote Francais : ");
} else {
System.out.print("\n\tChamps inexistant!");
}
etudiants[i][j] = saisie.nextLine();
}
}
System.out.print("\n\tEtudiants Enregistres : \n\n");
// System.out.print("\tCode\tNom\t\tMaths\tFrancais\n\n");
for (int i = 0; i < 1; i++) {
for (int j = 0; j < 4; j++) {
System.out.print("\t" + etudiants[i][j] + " ");
}
}
System.out.println();
System.out.print("\n\tEntrez code etudiant : ");
String recherche = saisie.nextLine();
boolean trouve = false;
for (int i = 0; i < 1; i++) {
for (int j = 0; j < 4; j++) {
if (recherche.equals(etudiants[i][0])) {
trouve = true;
System.out.print("\n\tCode etudiant correct!");
String math = etudiants[i][2];
String francais = etudiants[i][3];
Double m = new Double(math);
double mathConv = m.doubleValue();
Double f = new Double(francais);
double francaisConv = f.doubleValue();
double moyenne = (mathConv + francaisConv) / 2;
System.out.print("\n\tMoyenne de l'etudiant : " + moyenne);
System.out.print("\n\tEtudiant : " + etudiants[i][j]);
if (moyenne <= 40) {
System.out.print("\n\tEchec!");
} else if (moyenne > 40 && moyenne < 70) {
System.out.print("\n\tReprise!");
} else {
System.out.print("\n\tSucces!");
}
} if (!trouve) {
System.out.print("\n\tCode etudiant incorrect!");
}
}
}
}
}
I need to display only one message after entering the code etudiant but istead it displays the message 4 times. The loop should only iterate through the first column of each line and compares it to what the user entered.
The indexing variable (j) of the last inner for-loop (for (int j = 0; j < 4; j++) {) is never used inside the loop, so you actually do not need that loop at all. General styling issues of your sample aside, you should rewrite the last loop like this:
for (int i = 0; i < 1; i++) {
// throw away this
//for (int j = 0; j < 4; j++) {
if (recherche.equals(etudiants[i][0])) {
trouve = true;
// ... rest of the code like you currently have it
// You probably do not need this line too,
// because you have almost the same in your second loop
//System.out.print("\n\tEtudiant : " + etudiants[i][j]);
}
}
This is my first code with java .when I tested it on Ideone. It showed:
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:862)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at Hw2_p4.main(Main.java:22)
I searched for answers but I didn't get the problem cause or how to fix it.
the code runs on eclipse normally
Here is the code
import java.util.Scanner;
class Hw2_p4 {
static void swap(String[] A, int a, int b) {
String temp = A[a];
A[a] = A[b];
A[b] = temp;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int i, j, k, l, counter = 0;
String[] name = new String[16];
String[][] notalong = new String[120][2];
String[] temp = new String[120];
boolean[][] A = new boolean[120][2];
for (i = 0; i < n; i++) {
name[i] = sc.next();
}
for (i = 0; i < m; i++) {
for (j = 0; j < 2; j++) {
notalong[i][j] = sc.next();
}
}
int flag = 0;
for (i = 0; i < m; i++) {
for (j = 0; j < 2; j++) {
flag = 0;
for (k = i + 1; k < m; k++) {
for (l = 0; l < 2; l++) {
if (notalong[i][j].compareToIgnoreCase(notalong[k][l]) == 0 && A[i][j] == false && A[k][l] == false) {
A[k][l] = true;
flag = 1;
}
}
}
if (flag == 1) {
A[i][j] = true;
counter++;
} else if (flag == 0 && A[i][0] == false && A[i][1] == false) {
A[i][j] = true;
counter++;
}
}
}
System.out.println(n - counter);
int x = 0;
for (i = 0; i < m; i++) {
for (j = 0; j < 2; j++) {
if (A[i][j] == false) {
temp[x++] = notalong[i][j];
A[i][j] = true;
for (k = i + 1; k < m; k++) {
for (l = 0; l < 2; l++) {
if (notalong[i][j].compareToIgnoreCase(notalong[k][l]) == 0 && A[k][l] == false) {
A[k][l] = true;
}
}
}
}
}
}
//compare not along with names
int found = 1;
for (i = 0; i < n; i++) {
found = 0;
for (j = 0; j < m; j++) {
for (k = 0; k < 2; k++) {
if (name[i].compareToIgnoreCase(notalong[j][k]) == 0) {
found = 1;
}
}
}
if (found == 0) {
temp[x++] = name[i];
}
}
//sorting lexicographically
boolean swapp = true;
for (i = 0; i < x && swapp; i++) {
swapp = false;
for (j = 0; j < x - i - 1; j++) {
if (temp[j].compareToIgnoreCase(temp[j + 1]) > 0) {
swap(temp, j, j + 1);
swapp = true;
}
}
}
for (i = 0; i < x; i++) {
System.out.println(temp[i]);
}
}
}
Ideone is not interactive. You have to click on Specify input and enter all of your input in there before you run the application. What you are seeing is an exception because System.in has an "end of file" status.
You ought to protect sc.next() with cs.hasNext():
while (!sc.hasNext()) {
Thread.sleep(100);
}
int n = sc.nextInt();
However since you read the input in many location throughout your code try using Scanner.nextLine() preferably printing to the console, what is the input you are expecting next. nextLine() will read all the input until the user presses ENTER, which is a nicer 'user journey' than simply constantly reading keyboard and passing it to System.in.
System.out.print("Please enter your favourite number : "); //NB print and space is last
String input = sc.nextLine();
int favNumber = Double.parseDouble(input);
the way that Ideone gives input to your code is different from the way your IDE(or you)gives input.
so you should probably figure out how does Ideone gives input and then fix your code to that way of inputs.
What is the best(fastest) way to sort an array of Strings (using Java 1.3).
You can use this code for sort the string values,
public Vector sort(String[] e) {
Vector v = new Vector();
for(int count = 0; count < e.length; count++) {
String s = e[count];
int i = 0;
for (i = 0; i < v.size(); i++) {
int c = s.compareTo((String) v.elementAt(i));
if (c < 0) {
v.insertElementAt(s, i);
break;
} else if (c == 0) {
break;
}
}
if (i >= v.size()) {
v.addElement(s);
}
}
return v;
}
Also see this sample code for using bubble sort,
static void bubbleSort(String[] p_array) throws Exception {
boolean anyCellSorted;
int length = p_array.length;
String tmp;
for (int i = length; --i >= 0;) {
anyCellSorted = false;
for (int j = 0; j < i; j++) {
if (p_array[j].compareTo(p_array[j + 1]) > 0) {
tmp = p_array[j];
p_array[j] = p_array[j + 1];
p_array[j + 1] = tmp;
anyCellSorted = true;
}
}
if (anyCellSorted == false) {
return;
}
}
}
Use java.util.Arrays.sort.
If it's not possible for some reason due to limitations of the platform, you can get ideas from its source.