I am trying the ransom note challenge:
Given two strings ransomNote and magazine, return true if ransomNote can be constructed by using the letters from magazine and false otherwise.
Each letter in magazine can only be used once in ransomNote.
Example 1:
Input: ransomNote = "a", magazine = "b"
Output: false
Example 2:
Input: ransomNote = "aa", magazine = "ab"
Output: false
Example 3:
Input: ransomNote = "aa", magazine = "aab"
Output: true
here is my solution:
public static boolean canConstruct(String ransomNote, String magazine) {
ArrayList<Character> ransomChar = new ArrayList<Character>();
ArrayList<Character> magazineChar = new ArrayList<Character>();
if (ransomNote.length() == 1 && magazine.length() == 1) {
if (ransomNote.equals(magazine)) {
return true;
}
return false;
}
else if (ransomNote.length() == 1 && magazine.length() > 1) {
for (int i = 0; i < magazine.length(); i++) {
if (magazine.charAt(i) == ransomNote.charAt(0)) {
return true;
}
}
return false;
}
else if (ransomNote.length() > 1 && magazine.length() > 1) {
for (int i = 0; i < ransomNote.length(); i++) {
ransomChar.add(ransomNote.charAt(i));
}
for (int i = 0; i < magazine.length(); i++) {
magazineChar.add(magazine.charAt(i));
}
while (ransomChar.size() > 1) {
for (int i = 0; i < ransomChar.size(); i++) {
boolean flag = false;
for (int j = 0; j < magazineChar.size(); j++) {
if (ransomChar.get(i).equals(magazineChar.get(j))) {
ransomChar.remove(i);
magazineChar.remove(j);
flag = true;
}
else if (ransomChar.isEmpty()) {
return true;
}
}
if (!flag) {
return false;
}
}
}
if (ransomChar.size() == 1 && magazineChar.size() == 1) {
if (ransomChar.equals(magazineChar)) {
return true;
}
return false;
}
else if (ransomChar.size() == 1 && magazineChar.size() > 1) {
for (int i = 0; i < magazineChar.size(); i++) {
if (ransomChar.get(0).equals(magazineChar.get(i))) {
return true;
}
}
return false;
}
}
return false;
}
I am passing most test cases but it throws an error at input:
"bg"
"efjbdfbdgfjhhaiigfhbaejahgfbbgbjagbddfgdiaigdadhcfcj"
It throws error:
java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
at line: if (ransomChar.get(i).equals(magazineChar.get(j)))
Here's a simple way to attack your problem by building a set of candidate characters from magazine. Once you've done that, you just iterate over the ransomNote, checking the set for each character. If you find it, you remove it from the set. If you don't, then you return false. If you make it all the way through, you return true. You need to use a MultiSet because you need to be able to represent multiple copies of the same character in the magazine.
Here's how to do that:
public static boolean canConstruct(String ransomNote, String magazine) {
MultiSet<Character> magazineChars = new HashMultiSet<>();
for (int i = 0; i < magazine.length(); i++)
magazineChars.add(magazine.charAt(i));
for (int i = 0 ; i < ransomNote.length(); i++) {
Character c = ransomNote.charAt(i);
if (magazineChars.contains(c))
magazineChars.remove(c);
else
return false;
}
return true;
}
On each iteration of this loop:
for (int i = 0; i < ransomChar.size(); i++) {
boolean flag = false;
for (int j = 0; j < magazineChar.size(); j++) {
if (ransomChar.get(i).equals(magazineChar.get(j))) {
ransomChar.remove(i);
magazineChar.remove(j);
flag = true;
}
else if (ransomChar.isEmpty()) {
return true;
}
}
if (!flag) {
return false;
}
}
, you know that i is initially less than ransomChar.size(), so that it is safe to perform ransomChar.get(i).
However, the inner loop may remove one (or more!) elements of ransomChar, after which it may no longer be true that i is less than ransomChar.size(). The inner loop continues to iterate, and as a result, it can attempt to retrieve an element of ransomChar that does not exist.
Honestly, the code overall is a mess. Other than this specific issue,
I see no need why you need special cases for all the variations on sizes of note and magazine. I think you might write better code for this if you devoted some effort to designing an approach that just works correctly for all cases.
you are not making good use of the available features of the classes you are using
you are not making good use of general Java language features that would be applicable.
For example, I might write that particular loop as:
for (Character c : ransomChar) {
if (!magazineChar.remove(c)) {
return false;
}
}
return true;
And that's before we come to the fact that you've chosen a suboptimal algorithm. Its execution time will scale as note size * magazine size, whereas there are alternatives that will scale as note size + magazine size, instead. That may in fact be important, because choice of an efficient algorithm is one thing that the test cases for coding challenges such as this often try to detect.
Please find small and simple solution with Time complexity O(n) and Space complexity O(256) or O(1)
int[] count = new int[256];
for (char c : magazine.toCharArray()) {
count[c]++;
}
for (char c : ransomNote.toCharArray()) {
if (count[c]-- == 0) return false;
}
return true;
Result
#Harsh so you are trying to access the element of ransomChar which do not even exists. look at the lines
if (ransomChar.get(i).equals(magazineChar.get(j))) {
ransomChar.remove(i);
magazineChar.remove(j);
flag = true;
}
you are removing the elements from the lists and and every time you remove the elements from the list their indexing changes and and when it rechecks the condition the list is already empty after several iteration. Hence giving the run time error. to avoid this you should check whether the list is empty or not , first (you are checking it after).
Related
I have two parameters represent two case-sensitive words that might have been found in a document. I need a method that must return true if the first parameter has occurred at least once in the document and was immediately followed at least once by the second word. Otherwise it must return false.
Here is my code.
public boolean nextTo(String firstInput, String secondInput) {
Iterator it = Words.keySet().iterator();
while (it.hasNext() == true)
{
it.next();
if (Words.containsKey(firstInput))
{
if(Words.higherKey(secondInput))
{
return true;
}
}
}
return false;
Solution 1:
Represent the document as a String[] and just iterate the words in the document. When you see firstInput, test that the next word is secondInput.
Solution 2:
Create a reverse index for the document; i.e. a Map<String, int[]> where the String keys are the words and the int[] values contain the indexes for each occurrence of the word in the document.
Populate the reverse index from the document.
Then, to find if firstInput followed by secondInput:
int[] fi = reverseIndex.get(firstInput);
int[] si = reverseIndex.get(secondInput);
if (fi == null || si == null) {
return false;
}
for (int i = 0; i < fi.length; i++) {
for (int j = 0; j < si.length; j++) {
if (fi[i] == si[j] - 1) {
return true;
}
}
}
return false.
When i have a problem with the code im writing, i usually handle it like a story. Each command is a sentence in a story. The sentences needs to make sense in order for the story to be complete/right.
So im learning java from scratch now with the MOOC course at Helsinki University. I got somewhat stuck at exercise 68. The program is suppose to compare integer values of a list(array) together with user input. What i programmed is a method that return true if the user input number is already on the list, and false if its not.
What I said about story at the start: The commented out code is my initial code. This did not past the last test but in my head both the commented out code and the other code say basically the same
Error message (from last test):
"Answer wrong when parameter was list [0, 7, 9, -1, 13, 8, -1] and value 8 expected: false but was: true"
public static boolean moreThanOnce(ArrayList<Integer> list, int searched)
// if (list.size()==1) {return false;}
//
// for (int i = 0; i < list.size();i++ ){
// if (list.contains(searched))
//
// {
//
// return true; }
//
// }
//return false;
//
int counter = 0;
for (int num : list) {
if (searched == num) {
counter++;
}
}
if (counter >= 2){
return true;
} else {
return false;
}
}
I understand that there is something wrong, just cant seem to figure it out. Do you see why the last code would be accepted, but not the first (commented out one) ?
If any use, the rest of the code (not my work) is this:
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(3);
list.add(2);
list.add(7);
list.add(2);
System.out.println("Type a number: ");
int number = Integer.parseInt(reader.nextLine());
if (moreThanOnce(list, number)) {
System.out.println(number + " appears more than once.");
} else {
System.out.println(number + " does not appear more than once. ");
}
}
}
Code that is commented out guaranties only that if true there is at least one of occurance in array, maybe there are more but not guaranted. If function returns false thes may be 1 or no occurance.
Reason: If arrary os bigger than 1 it does not mean that there are 2 or more occurances of value you search for.
Posible solution: Add counter like uncommented code.
Your first algorithm has a few flaws, first you test for a length of one explicitly. Not null, and not an empty List. Second, you should prefer the List interface to the ArrayList explicit type. And finally, you need to consider the sublist offset by one of the current position when you call contains (clearly the list contains at least the current value).
I think you wanted something like
public static boolean moreThanOnce(List<Integer> list, int searched) {
if (list == null || list.size() < 2) {
return false;
}
int len = list.size();
for (int i = 0; i < len - 1; i++) {
if (list.get(i).equals(searched)
&& list.subList(i + 1, list.size()).contains(searched)) {
return true;
}
}
return false;
}
And, we can express that as generic method. Like,
public static <T> boolean moreThanOnce(List<T> list, T searched) {
if (list == null || list.size() < 2) {
return false;
}
int len = list.size();
for (int i = 0; i < len - 1; i++) {
if (list.get(i).equals(searched)
&& list.subList(i + 1, list.size()).contains(searched)) {
return true;
}
}
return false;
}
or, if you're using Java 8+, use a Stream and filter and then count like
public static <T> boolean moreThanOnce(List<T> list, T searched) {
if (list == null || list.size() < 2) {
return false;
}
return list.stream().filter(v -> v.equals(searched)).count() > 1;
}
I'm a student, and I've been working on the following challenge: find a substring (the needle) in a larger string (the haystack) without using the substring method, and using recursion. Recursion isn't my strong suit, but I have worked out the following:
public class Contains
{
public static void main(String[] args)
{
System.out.println(contains("Java programming", "ogr", false));
}
public static boolean contains(String haystack, String needle, boolean doesContain)
{
if(haystack.length() < needle.length())
{
return false;
}
else
{
for(int i = 0; i < needle.length(); i++)
{
if(haystack.charAt(i) != needle.charAt(i))
if((i + 1) == needle.length())
{
doesContain = false;
break;
}
else
break;
else
if((i + 1) == needle.length())
{
doesContain = true;
break;
}
else
continue;
}
char[] haystackChar = haystack.toCharArray();
char[] newCharArray = new char[(haystackChar.length - 1)];
for(int j = 1; j < haystackChar.length; j++)
{
newCharArray[j - 1] = haystackChar[j];
}
String newStr = new String(newCharArray);
if(doesContain == false)
contains(newStr, needle, doesContain);
}
return doesContain;
}
}
I realize this might not be the best or most elegant solution, but I am mostly just trying to get it to work. I've been running it in the Eclipse debugger, and everything is functioning as expected up until the call to if(doesContain == false) during the method call to contain where doesContain is set to true during the iteration of the for loop. The debugger is showing the value of doesContain to (correctly) be true, and it shows it skipping over the if statement, and exiting the else block. However, immediately after that, it jumps back up into the else block and only calls the recursive call to contain, instead of returning doesContain. Then, it continues to work recursively and subsequently fail and return false, because it's now searching through the rest of the string, where the "needle" is not located.
I know that StackOverflow is not a 'homework help' location per se, but I program for purposes other than school, and I'm quite perplexed as to why it's behaving this way. Does anyone know why it's doing this? Am I missing something here?
I took a look through your code and ran it in eclipse myself. A theory you will want to look into is how stacking works in recursion. Your program is finding true and then leaving the stack, but by that point it had reoccurred several times. It returned true, but then also went on to return all the false variables that were stored before it.
If you have any further questions please let me know.
EDIT
If you are really interested in getting into advanced recursion I highly recommend this video: Java Recursion
Hey, I didn't need to go that far to make it work. You can remove doesContain as a parameter and set it as a static instance variable and it worked for me.
public class Contains
{
private static boolean doesContain = false;
public static void main(String[] args)
{
System.out.println(contains("Java programming", "ogr"));
}
public static boolean contains(String haystack, String needle)
{
if(haystack.length() < needle.length())
{
return false;
}
else
{
for(int i = 0; i < needle.length(); i++)
{
if(haystack.charAt(i) != needle.charAt(i))
if((i + 1) == needle.length())
{
doesContain = false;
break;
}
else
break;
else
if((i + 1) == needle.length())
{
doesContain = true;
break;
}
else
continue;
}
char[] haystackChar = haystack.toCharArray();
char[] newCharArray = new char[(haystackChar.length - 1)];
for(int j = 1; j < haystackChar.length; j++)
{
newCharArray[j - 1] = haystackChar[j];
}
String newStr = new String(newCharArray);
if(doesContain == false)
contains(newStr, needle);
}
return doesContain;
}
}
What you had was very close, but by passing it as a parameter you were storing every time you went through another recursion. This way you only return your final value.
To find a needle in the haystack in the way you want, you don't need to use recursion.
Just remove the following lines of code from your function and it will work just fine:
char[] haystackChar = haystack.toCharArray();
char[] newCharArray = new char[(haystackChar.length - 1)];
for(int j = 1; j < haystackChar.length; j++)
{
newCharArray[j - 1] = haystackChar[j];
}
String newStr = new String(newCharArray);
if(doesContain == false)
contains(newStr, needle, doesContain);
I think you are sort of confusing yourself with the recursive function. One of the variables passed to the recursive function is doesContain, but the function is supposed to return whether the string contains it! In the lines
if(doesContain == false)
contains(newStr, needle, doesContain);
The call to contains will return if the substring contains the needle. You need to take that value, and return it back up the call stack.
Hopefully that made some sense. If that didn't, I'll give you the code so you can figure it out yourself:
public static boolean contains(String haystack, String needle)
{
if(haystack.length() < needle.length())
{
return false;
}
else
{
boolean doesContain=false;
for(int i = 0; i < needle.length(); i++)
{
if(haystack.charAt(i) != needle.charAt(i))
if((i + 1) == needle.length())
{
doesContain = false;
break;
}
else
break;
else
if((i + 1) == needle.length())
{
doesContain = true;
break;
}
else
continue;
}
char[] haystackChar = haystack.toCharArray();
char[] newCharArray = new char[(haystackChar.length - 1)];
for(int j = 1; j < haystackChar.length; j++)
{
newCharArray[j - 1] = haystackChar[j];
}
String newStr = new String(newCharArray);
if(doesContain == false)
return contains(newStr, needle);
else
return true;
}
}
public static boolean isValidNumber(String a1)
{
String x = ("0123456789");
boolean valid = false;
for (int i = 0; i < 4; i++) {
char c = a1.charAt(i);
for (int j = 0; j < 10; j++) {
if ( c == x.charAt(j)) {
valid = true;
}
else {
valid = false;
}
}
}
return valid;
}
The above method checks to see whether an input of a four character string is composed of the characters 0123456789. However, regardless of what the input is, the method always returns as false.
If I were to change the valid value in the else statement to true, the method would always return as true.
What is the error that I have made in this method?
As soon as you find a non matching character, break the loop otherwise the next matching character will set valid to true.
e.g. "123a456" is considered valid.
for (int j = 0; j < 10; j++) {
if ( c == x.charAt(j)) {
valid = true;
}
else {
valid = false;
break;
}
}
If for some reason you don't want to break the loop, you could keep an "invalid counter" and make sure that is 0 at the end.
Of course for what you are doing here, Integer.parseInt() might be your best bet ;-)
a String.equals method will check these two strings in a single statement if you are permitted to use that.
public static boolean isValidNumber(String a1)
{
String x = ("0123456789");
return x.equals(a1);
}
I would rewrite your function as given below,
String x = ("0123456789");
boolean valid = false;
for (int i = 0; i < 4; i++) {
char c = a1.charAt(i);
boolean isCharOK = false;
for (int j = 0; j < 10; j++) {
if ( c == x.charAt(j)) {
isCharOK = true;
break;
}
}
if (!isCharOK) {
valid = false;
break;
}
}
return valid;
John3136 is quite correct, but I would like to propose even better solution to your whole task:
final static String x = "0123456789";
public static boolean isValidNumber(String a1) {
for (int i = 0; i < a1.length(); ++i) {
if (x.indexOf(a1.charAt(i)) == -1) return false;
}
return true;
}
In short: the above code "looks up" every character in your parameter string a1 in the string composed of digits. If it can find it, continues. If it can't, it means a1 consist not only digits and returns false. If it passes through all a1 characters then it returns true :)
And as asked and described in the comments - handling of duplicate characters in argument string:
final static String x = "0123456789";
public static boolean isValidNumber(String a1) {
for (int i = 0; i < a1.length(); ++i) {
final char currentChar = a1.charAt(i);
if (x.indexOf(currentChar) == -1 || a1.indexOf(currentChar, i+1) != -1)
return false;
}
return true;
}
The function call a1.indexOf(currentChar, i+1) essentially checks if there is any duplicate character in the rest of the string (from position i+1 and farther). Which means if it will be able to find duplicate char, the method return false :) Hope this helps, here is more info on String.indexOf(int, int) function if you want:
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#indexOf(int, int)
You can use this one liner function to check for validity of a String as Number using Regular Expression
public static boolean isValidNumber(String a1)
{
return a1.matches("[\\d]+");
}
Hope this helps.
Good day stack overflow.
I'm a noob in using regex and here is my problem - I need to check a password if it contains 4 consecutive characters. so far what I have just covered is regarding the digits. Here is my regex:
ascending digits - ^.?(?:0123|1234|2345|3456|4567|5678|6789).$
descending digits - ^.?(?:9876|8765|7654|6543|5432|4321|3210).$
This works only for the digits. I know this is already an overkill in regex so I dont want to do it with the letters. It will be waaay too overkill if I do that.
abcdblah //true because of abcd
helobcde //true because of bcde
dcbablah //true beacause of dcba
heloedcb //true because of edcb
Any help would be highly appreciated. Thanks stackoverflow.
The answer is simple: don't use regexes.
Use this approach:
iterate over each letter (of course, skip the last tree letters)
iterate over the next three letters and check for ascending order
if they all were ascending return true.
iterate over the next three letters and check for descending order
if they all were descending return false.
return false
In code, this would look like this (untested code):
public boolean checkForAscendingOrDescendingPart(String txt, int l)
{
for (int i = 0; i <= txt.length() - l; ++i)
{
boolean success = true;
char c = txt.charAt(i);
for (int j = 1; j < l; ++j)
{
if (((char) c + j) != txt.charAt(i + j))
{
success = false;
break;
}
}
if (success) return true;
success = true;
for (int j = 1; j < l; ++j)
{
if (((char) c - j) != txt.charAt(i + j))
{
success = false;
break;
}
}
if (success) return true;
}
return false;
}
Good luck!
StackOverflow :)
here is an idea that doesn't use regex:
all characters have an ansi value and usually consecutive. so abcd should have let's say the following ansi values:64,65,66,67
pseudocode:
for (i=string.start;i<string.end-4;i++) {
check=string.substring(i,4);
c1=check.substring(0,1);
c2=check.substring(1,1);
c3=check.substring(2,1);
c4=check.substring(3,1);
if (c1.ansival==c2.ansival+1 && c2.ansival==c3.ansival+1 && c3.ansival==c4.ansival+1) {
return false;
} else {
return true;
}
}
also repeat in reverse order (c1.ansival+1==c2.ansival) for descending order
There is no way to solve this using regexes apart from the "overkill" solution of listing each of the possible sequences you want to match. Regexes are not expressive enough to offer a better solution.
This is my solution. It uses only a single loop.
Keep in mind that you'll need more logic if you want to constrain it to pure ASCII.
static boolean isWeak(String pass) {
Character prev = null;
Boolean asc = null;
int streak = 0;
for (char c : pass.toCharArray()) {
if (prev != null) {
switch (c - prev) {
case -1:
if (Boolean.FALSE.equals(asc)) streak++;
else { asc = false; streak = 2; }
break;
case 1:
if (Boolean.TRUE.equals(asc)) streak++;
else { asc = true; streak = 2; }
break;
default: asc = null; streak = 0;
}
if (streak == 4) return true;
}
prev = c;
}
return false;
}
Consider this
String s = "aba";
for (int i = 0; i < s.length() - 1; i++) {
if (!(Character.isLetter(c1) && Character.isLetter(c2))) {
//reject
}
if ((int)s.charAt(i) > (int)s.charAt(i + 1))) {
//reject
}
}
for s the if statement would be true so you could reject it. If s was abc then the if statement would never be true.
The code above using the > checks for ascending order. Use < for descending order