I have to write a method that returns the count of 'a' chars in the matrix but I have to do it with using recursion and it must be O(n.log(n)).
This is the my non-recursive code:
public static int acount(char[][] mat) {
int result = 0;
int i = 0;
int j = 0;
while (i <= 4) {
if (mat[i][j] == 'a') {
result++;
j++;
}
if (mat[i][j] == 'b') {
i++;
j = 0;
}
}
return result;
}
This is what i have tried but there was an error: "Exception in thread "main" java.lang.StackOverflowError":
public static int acount(char[][] mat) {
int result = 0;
int i = 0;
int j = 0;
while (mat[i][j] == 'a') {
result++;
j++;
}
if (i < 5) {
i++;
j = 0;
} else {
return result;
}
return acount(mat);
}
This is the main code:
public static void main(String[] args) {
int n = 5;
Random rand = new Random();
char[][] input = new char[n][n];
for (int i = 0; i < n; i++) {
int a_num = rand.nextInt(n);
for (int j = 0; j < a_num; j++) {
input[i][j] = 'a';
}
for (int j = a_num; j < n; j++) {
input[i][j] = 'b';
}
}
System.out.println(Arrays.deepToString(input));
System.out.println(acount(input));
}
}
How can it resolved?
Okay, let's start with your original version.
public static int acount(char[][] mat) {
int result = 0;
int i = 0;
int j = 0;
while (i <= 4) {
if (mat[i][j] == 'a') {
result++;
j++;
}
if (mat[i][j] == 'b') {
i++;
j = 0;
}
}
return result;
}
This code looks problematic. First, it assumes that your input only contains a or b. If you include other data, you'll never increment either i or j and you loop forever. It also looks like you're assuming that each row in the matrix is a-a-b-who-cares. That is, all a's, but you stop looking at the first b. Is that what you really want? Maybe it is.
I would have written this as:
for (int index1 = 0; index1 < 5; ++index1) {
for (int index2 = 0; index2 < 5; ++index2) {
if (mat[index1][index2] == 'a') {
++result;
}
}
}
And actually, i wouldn't hard-code 5 but would instead use mat.length and mat[index1].length respectively.
As for your stack overflow -- this is because your recursion never ends. You do this:
return acount(mat);
That is, you call acount with the same array again, which calls acount again, which calls acount again...
The only way that doesn't fail is if your code above returns early, which it probably won't. I think what you're missing is that each time you enter acount, you get fresh variables, so i and j start out freshly as zeros again.
You could partially solve this by passing i into acount, and when you recurse:
return acount(mat, i+1);
That will fix some of the problem, although I think you're complexity is still O(n^2).
Then let's look at this:
while (mat[i][j] == 'a') {
result++;
j++;
}
What happens if the row never has any a's in it? It looks past the end of the array. You're making assumptions about the input data, which is going to bite you.
Related
I have a factorial function on my program that works fine until i try to execute the function deleteRepeated(), the console is telling me that the error is in the return of the factorial function, maybe it's being called by a single function too many times in a short period of time? I've been stuck for hours.
import java.util.Scanner;
public class ex9 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
int[] newArr = new int[n - repeated(arr)];
int[] finalArr = deleteRepeated(arr, newArr);
for (int a : finalArr) {
System.out.println(a);
}
}
public static long factorial(int n) {
if (n == 0)
return 1;
return (n * factorial(n - 1));
}
public static int repeated(int arr[]) {
int n = arr.length;
int mix = (int) (factorial(n) / (2 * factorial(n - 2)));
int i = 0;
int k = 0;
int rep = 0;
int a = -100;
while (i < mix) {
for (int j = k + 1; j < n; j++) {
if (arr[k] == arr[j] && a != j) {
a = j;
rep += 1;
}
i++;
}
k++;
}
return rep;
}
public static int[] deleteRepeated(int arr[], int newArr[]) {
int n = arr.length;
int rep = repeated(arr);
int i = 0;
int k = 0;
int a = -100;
while (i < newArr.length) {
for (int j = k + 1; j < n; j++) {
if (arr[k] == arr[j] && a != arr[k]) {
a = arr[j];
newArr[k] = arr[k];
}
i++;
}
k++;
}
rep = repeated(newArr);
if (rep > 0) {
int[] newArr2 = new int[newArr.length - rep];
deleteRepeated(newArr, newArr2);
}
return newArr;
}
}
Only thing i could do to avoid the error was stopping the function from executing :/, maybe it has to do with how i'm re-calling it at the end of each execution...? is what i did allowed?
So, deleteRepeated is all messed up. The issue is deleteRepeated does not actually remove duplicate elements, so the check for the base case of recursion always fails. I'm not sure why you're using recursion here anyway; if the while loop worked properly, it could remove all duplicates without need for recursion.
It appears that you copy-pasted the implementation of repeated into deleteRepeated, and you replaced the logic for handling repeated elements with logic that handles non-repeated elements.
Here is how I would implement the method:
public static int deleteRepeated(int arr[], int newArr[]) {
int n = 0;
for(int i = 0; i < arr.length; i++) {
boolean unique = true;
for(int j = 0; j < n; j++)
unique = unique && newArr[j] != arr[i];
if(unique)
newArr[n++] = arr[i];
if(n >= newArr.length) break;
}
return n;
}
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 tried to code the naive solution for that, which tries to match a first index and then go deeper.
But I get true when I shouldn't and I cant find why.
this is my code (Java):
boolean contains(BufferedImage img, BufferedImage subImg, int[] coordinates){
boolean result = false;
int verticalLimit = img.getWidth() - subImg.getWidth();
int horizontalLimit =img.getHeight() - subImg.getHeight();
for (int i = 0; i <= horizontalLimit; i++) {
for (int j = 0; j <= verticalLimit; j++) {
if(img.getRGB(j, i) == subImg.getRGB(0, 0)){
result = true;
coordinates[0] = j; // stores the first indices for self use
coordinates[1] = i;
for (int k = i; k < subImg.getHeight() && result; k++) {
for (int l = j; l < subImg.getWidth() && result; l++) {
if(img.getRGB(l, k) != subImg.getRGB(l, k)){
result = false;
}
}
}
if(result) return result;
}
}
}
return result;
}
your search for the sub image is off. you jump way far into the sub image by indexing with k,l i've changed to 0 and using k,l as offsets from i,j. also use a labeled break from having to hold "found" state. if all of the pixels match it reaches the end of the loop and returns true otherwise it breaks and tries again until all possible locations are tried and returns false if none found.
static boolean contains(BufferedImage img, BufferedImage subImg, int[] coordinates) {
int verticalLimit = img.getWidth() - subImg.getWidth();
int horizontalLimit = img.getHeight() - subImg.getHeight();
for (int i = 0; i <= horizontalLimit; i++) {
for (int j = 0; j <= verticalLimit; j++) {
subSearch:
for (int k = 0; k < subImg.getHeight(); k++) {
for (int l = 0; l < subImg.getWidth(); l++) {
if (img.getRGB(l + j, k + i) != subImg.getRGB(l, k)) {
break subSearch;
}
}
if (k==subImg.getHeight()-1){
coordinates[0] = j;
coordinates[1] = i;
return true;
}
}
}
}
return false;
}
int[] value = new int[5];
boolean result = true;
for(int i = 0; i < 5; i++) {
value[i] = cards[i].getValue();
}
for(int i = 0; i < 5; i++) {
for(int j = i;j < 5; j++) {
if(value[i] == value[j + 1]) {
result = false;
}
}
}
return result;
This code is essentially going to compare the values each card object has, and if two cards in the array have the same value return true. We have 5 cards in each hand and that is why the array length is 5. The getValue method returns an integer which is essentially the value of the card. I don't seem to know what I'm doing wrong to be getting errors on my method.
Your array access is incorrect when you use j + 1, that will be out of bounds when j is four (at the end of the length for value). And, I would prefer to use value.length instead of hardcoding. Something like
for (int i = 0; i < value.length - 1; i++) {
for (int j = i + 1; j < value.length; j++) {
if (value[i] == value[j]) {
result = false;
}
}
}
Additionally, as pointed out by Tom, in the comments; it is pointless to continue iteration when the result becomes false. You could simply return when it becomes false and avoid the result variable entirely. Like,
for (int i = 0; i < value.length - 1; i++) {
for (int j = i + 1; j < value.length; j++) {
if (value[i] == value[j]) {
return false;
}
}
}
return true;
Another option, in Java 8+, would be something like
return IntStream.of(value).allMatch(x -> value[0] == x);
So here is my solution code:
public int count_two_char(String s, char c, char d){
int count = 0;
int count1 = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == c) {
count = count + 1;
}
for (int i1 = 0; i1 < s.length(); i1++) {
if (s.charAt(i1) == d) {
count1 = count1 + 1;
}
}
}
return count + count1;
}
And here is my test code:
#Test
public void tests3() {
code.Solution s = new code.Solution();
String input = "llss";
int expected = 4;
char c ='l';
char d ='s';
int actual = s.count_two_char(input, c, d);
assertTrue("Expected was" +expected+"but the actual was" +actual , expected == actual);
}
But when I test it. It shows a error which is expected was 4 but the actual was 10. why?? I'm so confuse right now.
Because your braces aren't indented well, it appears that you have 2 separate for loops. But after lining them up better:
for(int i=0; i<s.length(); i++){
if(s.charAt(i) == c){
count = count + 1;
}
for(int i1=0; i1<s.length(); i1++){
if(s.charAt(i1) == d){
count1 = count1 + 1;
}
}
}
You can see that the second for loop is nested inside the first for loop, so your count1 will be higher than expected.
Move that last brace between the for loops and they will be unnested. That should reduce the count returned so that your test passes.
This is because you have a for loop that you do not need. Both ifs should go into the same loop, like this:
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == c) {
count = count + 1;
}
if (s.charAt(i) == d) {
count1 = count1 + 1;
}
}
As it stands, the number of d characters is multiplied by the length of the string, producing 2+4*2=10.
You can make your code more idiomatic to the language if you replace count = count+1 with the equivalent count++. You could also drop count1 altogether, because you add the two counts at the end anyway.