There is a problem under string domain in Hacker Rank, named as Save Humanity
with Link
https://www.hackerrank.com/contests/princetonolympics/challenges/save-humanity.
The are two strings given under the condition that if the two strings are equals its returns true and if there is a one-bit-error in the string then it returns true with the error indices.
Otherwise False for every other case.
My solution works fine for some test-cases,but for some test-cases the result is a timeout.
The question is how to decrease the Complexity.
For checking the one-bit-error I am using charAt function. Due to this the complexity rises.
Please help.
If I understand you correctly you have two strings that you want to compare, but you want to accept them even if the they differ in one character.
This can be done in one loop by just comparing the strings byte by byte.
char[] a = "abc".toCharArray();
char[] b = "abb".toCharArray();
boolean oneDiff = false;
for (int i = 0; i<){
if(a[i] != b[i]){
if(oneDiff)
return false;
oneDiff = true;
}
}
return true;
This has a time complexity of just O(n), which should be fast enough for most cases. If you need faster algorithms you can maybe research Edit Distance which is the name of this problem in the general case, but I don't think there are any faster algorithms.
Related
I am trying to solve this question on LeetCode:
A string s is nice if, for every letter of the alphabet that s contains, it appears both in uppercase and lowercase. For example, "abABB" is nice because 'A' and 'a' appear, and 'B' and 'b' appear. However, "abA" is not because 'b' appears, but 'B' does not.
Given a string s, return the longest substring of s that is nice. If there are multiple, return the substring of the earliest occurrence. If there are none, return an empty string.
For s = "YazaAay", the expected output is: "aAa"
One of the top voted solutions uses a Divide and Conquer approach:
class Solution {
public String longestNiceSubstring(String s) {
if (s.length() < 2) return "";
char[] arr = s.toCharArray();
Set<Character> set = new HashSet<>();
for (char c: arr) set.add(c);
for (int i = 0; i < arr.length; i++) {
char c = arr[i];
if (set.contains(Character.toUpperCase(c)) && set.contains(Character.toLowerCase(c))) continue;
String sub1 = longestNiceSubstring(s.substring(0, i));
String sub2 = longestNiceSubstring(s.substring(i+1));
return sub1.length() >= sub2.length() ? sub1 : sub2;
}
return s;
}
}
I understand how it works, but not the intuition behind using a Divide and Conquer approach. In other words, if I revisit the problem again after a few days/weeks after I have forgotten everything about it, I won't be able to realize it is a Divide and Conquer problem.
What is that 'thing' that makes it solvable by a Divide and Conquer approach?
This is how the algorithm could be described in plain English:
If the entire string is nice, we are done.
Otherwise, there must be a character which exists in only one case. Such a character naturally divides the string into two substrings. Conquer each of them individually, and compare results.
Edit: BTW, I don't think it is a good example of D&C problem. The point is, once we encounter the first "bad" character, the substring to the left of it is nice. There is no need to descend into it. Just record its length and keep going. A simple loop it is.
Divide-And-Conquer, to paraphrase wikipedia, is most appropriate when a problem can be broken down into "2 or more subproblems". The solution here checks that the input string meets the condition, then breaks it in two at each character, and recursively checks the strings meet the condition until there is no solution. Generally, the application of divide-and-conquer is easy to get a feel for when the problem can be subdivided symmetrically, such as in the DeWall algorithm for computing the delaunay triangulation for a set of points (http://vcg.isti.cnr.it/publications/papers/dewall.pdf - cool stuff).
What sets the substring problem apart in this instance is it checks all (edit:) possible viable subdivisions by incrementing the line of subdivision. To clarify for anyone who might be confused, this is necessary because the string can't be split down the middle, else you might be splitting a substring like "aAaA" apart and returning only half of it in the end. This kind of meets the more condition in "two or more problems", but I agree it's not intuitive in this instance.
Hope this helps, I had to learn about this a lot recently while implementing the referenced algorithm. Someone with more experience might have a better answer.
This question already has answers here:
How do I determine whether an array contains a particular value in Java?
(30 answers)
Closed 2 years ago.
I'm working on a little project in java, and I want to make my algorithm more efficient.
What I'm trying to do is check if a given string is present in an array of strings.
The thing is, I know a few ways to check if a string is present in an array of strings, but the array I am working with is pretty big (around 90,000 strings) and I am looking for a way to make the search more efficient, and the only ways I know are linear search based, which is not good for an array of this magnitude.
Edit: So I tried implementing the advices that were given to me, but the code i wrote accordingly is not working properly, would love to hear your thoughts.`
public static int binaryStringSearch(String[] strArr, String str) {
int low = 0;
int high = strArr.length -1;
int result = -1;
while (low <= high) {
int mid = (low + high) / 2;
if (strArr[mid].equals(str)) {
result = mid;
return result;
}else if (strArr[mid].compareTo(str) < 0) {
low = mid + 1;
}else {
high = mid - 1;
}
}
return result;
}
Basically what it's supposed to do is return the index at which the string is present in the array, and if it is not in the array then return -1.
So you have a more or less fixed array of strings and then you throw a string at the code and it should tell you if the string you gave it is in the array, do I get that right?
So if your array pretty much never changes, it should be possible to just sort them by alphabet and then use binary search. Tom Scott did a good video on that (if you don't want to read a long, messy text written by someone who isn't a native english speaker, just watch this, that's all you need). You just look right in the middle and then check - is the string you have before or after the string in the middle you just read? If it is already precisely the right one, you can just stop. But in case it isn't, you can eliminate every string after that string in case it's after the string you want to find, otherwise every string that's before the just checked string. Of course, you also eliminate the string itself if it's not equal because - logic. And then you just do it all over again, check the string in the middle of the ones which are left (btw you don't have to actually delete the array items, it's enough just to set a variable for the lower and upper boundary because you don't randomly delete elements in the middle) and eliminate based on the result. And you do that until you don't have a single string in the list left. Then you can be sure that your input isn't in the array. So this basically means that by checking and comparing one string, you can't just eliminate 1 item like you could with checking one after the other, you can remove more then half of the array, so with a list of 256, it should only take 8 compares (or 9, not quite sure but I think it takes one more if you don't want to find the item but know if it exists) and for 65k (which almost matches your number) it takes 16. That's a lot more optimised.
If it's not already sorted and you can't because that would take way too long or for some reason I don't get, then I don't quite know and I think there would be no way to make it faster if it's not ordered, then you have to check them one by one.
Hope that helped!
Edit: If you don't want to really sort all the items and just want to make it a bit (26 times (if language would be random)) faster, just make 26 arrays for all letters (in case you only use normal letters, otherwise make more and the speed boost will increase too) and then loop through all strings and put them into the right array matching their first letter. That way it is much faster then sorting them normally, but it's a trade-off, since it's not so neat then binary search. You pretty much still use linear search (= looping through all of them and checking if they match) but you already kinda ordered the items. You can imagine that like two ways you can sort a buncha cards on a table if you want to find them quicker, the lazy one and the not so lazy one. One way would be to sort all the cards by number, let's just say the cards are from 1-100, but not continuously, there are missing cards. But nicely sorting them so you can find any card really quickly takes some time, so what you can do instead is making 10 rows of cards. In each one you just put your cards in some random order, so when someone wants card 38, you just go to the third row and then linearly search through all of them, that way it is much faster to find items then just having them randomly on your table because you only have to search through a tenth of the cards, but you can't take shortcuts once you're in that row of cards.
Depending on the requirements, there can be so many ways to deal with it. It's better to use a collection class for the rich API available OOTB.
Are the strings supposed to be unique i.e. the duplicate strings need to be discarded automatically and the insertion order does not matter: Use Set<String> set = new HashSet<>() and then you can use Set#contains to check the presence of a particular string.
Are the strings supposed to be unique i.e. the duplicate strings need to be discarded automatically and also the insertion order needs to be preserved: Use Set<String> set = new LinkedHashSet<>() and then you can use Set#contains to check the presence of a particular string.
Can the list contain duplicate strings. If yes, you can use a List<String> list = new ArrayList<>() to benefit from its rich API as well as get rid of the limitation of fixed size (Note: the maximum number of elements can be Integer.MAX_VALUE) beforehand. However, a List is navigated always in a sequential way. Despite this limitation (or feature), the can gain some efficiency by sorting the list (again, it's subject to your requirement). Check Why is processing a sorted array faster than processing an unsorted array? to learn more about it.
You could use a HashMap which stores all the strings if
Contains query is very frequent and lookup strings do not change frequently.
Memory is not a problem (:D) .
The question was asking me to return set containing all the possible combination of strings made up of "cc" and "ddd" for given length n.
so for example if the length given was 5 then set would include "ccddd" and "dddcc".
and length 6 would return set containing "cccccc","dddddd"
and length 7 would return set contating "ccdddcc","dddcccc","ccccddd"
and length 12 will return 12 different combination and so on
However, set returned is empty.
Can you please help?
"Please understand extremeply poor coding style"
public static Set<String> set = new HashSet<String>();
public static Set<String> generateset(int n) {
String s = strings(n,n,"");
return set; // change this
}
public static String strings(int n,int size, String s){
if(n == 3){
s = s + ("cc");
return "";}
if(n == 2){
s = s + ("ddd");
return "";}
if(s.length() == size)
set.add(s);
return strings(n-3,size,s) + strings(n-2,size,s);
}
I think you'll need to rethink your approach. This is not an easy problem, so if you're extremely new to Java (and not extremely familiar with other programming languages), you may want to try some easier problems involving sets, lists, or other collections, before you tackle something like this.
Assuming you want to try it anyway: recursive problems like this require very clear thinking about how you want to accomplish the task. I think you have a general idea, but it needs to be much clearer. Here's how I would approach the problem:
(1) You want a method that returns a list (or set) of strings of length N. Your recursive method returns a single String, and as far as I can tell, you don't have a clear definition of what the resulting string is. (Clear definitions are very important in programming, but probably even more so when solving a complex recursive problem.)
(2) The strings will either begin with "cc" or "ddd". Thus, to form your resulting list, you need to:
(2a) Find all strings of length N-2. This is where you need a recursive call to get all strings of that length. Go through all strings in that list, and add "cc" to the front of each string.
(2b) Similarly, find all strings of length N-3 with a recursive call; go through all the strings in that list, and add "ddd" to the front.
(2c) The resulting list will be all the strings from steps (2a) and (2b).
(3) You need base cases. If N is 0 or 1, the resulting list will be empty. If N==2, it will have just one string, "cc"; if N==3, it will have just one string, "ddd".
You can use a Set instead of a list if you want, since the order won't matter.
Note that it's a bad idea to use a global list or set to hold the results. When a method is calling itself recursively, and every invocation of the method touches the same list or set, you will go insane trying to get everything to work. It's much easier if you let each recursive invocation hold its own local list with the results. Edit: This needs to be clarified. Using a global (i.e. instance field that is shared by all recursive invocations) collection to hold the final results is OK. But the approach I've outlined above involves a lot of intermediate results--i.e. if you want to find all strings whose length is 8, you will also be finding strings whose length is 6, 5, 4, ...; using a global to hold all of those would be painful.
The answer to why set is returned empty is simply follow the logic. Say you execute generateset(5); which will execute strings(5,5,"");:
First iteration strings(5,5,""); : (s.length() == size) is false hence nothing added to set
Second iteration strings(2,5,""); : (n == 2) is true, hence nothing added to set
Third iteration strings(3,5,""); : (n == 3) is true, hence nothing added
to set
So set remains un changed.
I have this code, but it seems pretty unwieldy. Is there a more canonical way of doing so in Java?
public boolean oneDiff(String from, String s) {
if (from.length()!=s.length()) return false;
int differences = 0;
for (int charIndex = 0;charIndex<from.length();charIndex++) {
if (from.charAt(charIndex)!=s.charAt(charIndex)) differences++;
}
return (differences==1);
}
I agree with #mk. However to minimize the loop execution you should not run the loop till the string ends. Instead you can break the loop as soon as the difference becomes greater than 1. Like this:
for (int charIndex = 0;charIndex<from.length();charIndex++) {
if (from.charAt(charIndex)!=s.charAt(charIndex)) differences++;
if(differences > 1) break;
}
return (differences==1);
This will help in faster execution by loop optimization if this is what you want.
Nope, that really is the best way!
There's nothing built-in because this isn't something you need to do often. The closest trick is doing an xor on two integers, and then getting the Hamming Weight using bitCount, in order to check for how many flipped bits they have in common:
Integer.bitCount(int1 ^ int2)
But there's nothing like that for Strings - it's not a common case, so you have to code your own. And the way you've coded it seems fine - you really do have to loop over every character. I guess you could shorten the variable names and remove the parens around your return, but that's just cosmetic.
My roommate's teacher gave them a assignment to implement string length method in JAVA?
we have thought out two ways.
Check the element,and when get the out of bounds exception,it means the end of string,we catch this exception,then we can get the length.
Every time a string is pass to calculate the length,we add the special character to the end of it,it can be '\0',or "A",etc..
But we all think this two way may can finish the assignment,but they are bad(or bad habit to do with exception),it's not cool.
And we have googled it,but don't get what we want.
Something like this?
int i = 0;
for (char ch : string.toCharArray()) {
i++;
}
The pseudo-code you probably want is:
counter = 0
for(Character c in string) {
counter = counter + 1
}
This requires you to find a way to turn a Java String into an array of characters.
Likely the teacher is trying to make his or her students think, and will be satisfied with creative solutions that solve the problem.
None of these solutions would be used in the real world, because we have the String.length() method. But the creative, problem-solving process you're learning would be used in real development.
"1. Check the element,and when get the out of bounds exception,it means the end of string,we catch this exception,then we can get the length."
Here, you're causing an exception to be thrown in the normal case. A common style guideline is for exceptions to be thrown only in exceptional cases. Compared to normal flow of control, throwing an exception can be more expensive and more difficult to follow by humans.
That said, this one of your ideas has a potential advantage for very long strings. All of the posted answers so far run in linear time and space. The time and/or additional space they take to execute is proportional to the length of the string. With this approach, you could implement an O(log n) search for the length of the string.
Linear or not, it's possible that the teacher would find this approach acceptable for its creativity. Avoid if the teacher has communicated the idea that exceptions are only for exceptional cases.
"2. Every time a string is pass to calculate the length,we add the special character to the end of it,it can be '\0',or "A",etc.."
This idea has a flaw. What happens if the string contains your special character?
EDIT
A simple implementation would be to get a copy of the underlying char array with String.toCharArray(), then simply take its length. Unlike your ideas, this is not an in-place approach - making the copy requires additional space in memory.
String s = "foo";
int length = s.toCharArray().length;
Try this
public static int Length(String str) {
str = str + '\0';
int count = 0;
for (int i = 0; str.charAt(i) != '\0'; i++) {
count++;
}
return count;
}
What about:
"your string".toCharArray().length