A string S of lowercase letters is given. We want to partition this string into as many parts as possible so that each letter appears in at most one part, and return a list of integers representing size of each part.
Input: S = "ababcbacadefegdehijhklij"
Output: [9,7,8]
Explanation:
The partition is "ababcbaca", "defegde", "hijhklij".
This is a partition so that each letter appears in at most one part.
A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts.
Below is my Code for the above problem:
class Solution {
public List<Integer> partitionLabels(String S) {
char[] st = S.toCharArray();
int k=0,c=0;
List<Integer> res = new ArrayList<Integer> ();
Set<Integer> visited = new HashSet<Integer> ();
for(int i=0 ; i<st.length ; i++)
{
int idx = S.lastIndexOf(st[i]);
if(visited.add(i) && idx>i && idx>k)
{
k = Math.max(k,idx);
visited.add(k);
}
else if(i == k)
{
res.add(i-c+1);
c=i+1;
k++;
}
}
return res;
}
}
The above code works and the time complexity of the above code in O(n) since it visits each element once.
But what is the space complexity? Since I am using a Char array whose size is the same as the the String S and a Set whose Max size can be the size of the String S, is it also O(n)?
As you only use one dimensional arrays and lists your space complexity is O(n).
But your time complexity is O(n²) because S.lastIndexOf(st[i]); is O(n).
If you wanted also time to be O(n) you have to pre-process the string once (= O(n)) to determine the last occurence of each character, f.i. with a map to keep the retrieving time O(1).
Related
I would like to find the most efficient algorithm for this problem:
Given a string str and a list of strings lst that consists of only lowercase English characters and is sorted lexicographically, find all the words in lst that are a permutation of str.
for example:
str = "cat", lst = {"aca", "acc", "act", "cta", "tac"}
would return: {"act", "cta", "tac"}
I already have an algorithm that doesn't take advantage of the fact that lst is lexicographically ordered, and I am looking for the most efficient algorithm that takes advantage of this fact.
My algorithm goes like this:
public List<String> getPermutations(String str, List<String> lst){
List<String> res = new ArrayList<>();
for (String word : lst)
if (checkPermutation(word, str))
res.add(word);
return res;
}
public boolean checkPermutation(String word1, String word2) {
if (word1.length() != word2.length())
return false;
int[] count = new int[26];
int i;
for (i = 0; i < word1.length(); i++) {
count[word1.charAt(i) - 'a']++;
count[word2.charAt(i) - 'a']--;
}
for (i = 0; i < 26; i++)
if (count[i] != 0) {
return false;
}
return true;
}
Total runtime is O(NK) where N is the number of strings in lst, and k is the length of str.
One simple optimisation (that only becomes meaningful for really large data sets, as it doesn't really improve the O(NK):
put all the characters of your incoming str into a Set strChars
now: when iterating the words in your list: fetch the first character of each entry
if strChars.contains(charFromListEntry): check whether it is a permutation
else: obviously, that list word can't be a permutation
Note: the sorted ordering doesn't help much here: because you still have to check the first char of the next string from your list.
There might be other checks to avoid the costly checkPermutation() run, for example to first compare the lengths of the words: when the list string is shorter than the input string, it obviously can't be a permutation of all chars.
But as said, in the end you have to iterate over all entries in your list and determine whether an entry is a permutation. There is no way avoiding the corresponding "looping". The only thing you can affect is the cost that occurs within your loop.
Finally: if your List of strings would be a Set, then you could "simply" compute all permutations of your incoming str, and check for each permutation whether it is contained in that Set. But of course, in order to turn a list into a set, you have to iterate that thing.
Instead of iterating over the list and checking each element for being a permutation of your string, you can iterate over all permutations of the string and check each presence in the list using binary search.
E.g.
public List<String> getPermutations(String str, List<String> lst){
List<String> res = new ArrayList<>();
perm(str, (1L << str.length()) - 1, new StringBuilder(), lst, res);
return res;
}
private void perm(String source, long unused,
StringBuilder sb, List<String> lst, List<String> result) {
if(unused == 0) {
int i = Collections.binarySearch(lst, sb.toString());
if(i >= 0) result.add(lst.get(i));
}
for(long r = unused, l; (l = Long.highestOneBit(r)) != 0; r-=l) {
sb.append(source.charAt(Long.numberOfTrailingZeros(l)));
perm(source, unused & ~l, sb, lst, result);
sb.setLength(sb.length() - 1);
}
}
Now, the time complexity is O(K! × log N) which is not necessarily better than the O(NK) of your approach. It heavily depends on the magnitude of K and N. If the string is really short and the list really large, it may have an advantage.
There are a lot of optimizations imaginable. E.g. instead constructing each permutation, followed by a binary search, each recursion step could do a partial search to identify the potential search range for the next step and skip when it’s clear that the permutations can’t be contained. While this could raise the performance significantly, it can’t change the fundamental time complexity, i.e. the worst case.
In the book "Cracking the Coding Interview", first exercise says "Implement an algorithm to determine if a string has all unique characters (not using additional data structures)". And the solution:
public static boolean isUniqueChars(String str) {
boolean [] char_set = new boolean[256];
for (int i = 0; i < str.length(); i++) {
int val = str.charAt(i);
if (char_set[val]) return false;
char_set[val] = true;
}
return true;
}
Then they say "Time complexity is O(n), where n is the length of the string, and space complexity is O(n)".
I don't see why space complexity is O(n). The array char_set has a constant length, independent on how long the given str is. To me, space complexity is O(1).
Its space complexity is O(1) (\Theta(1)) as it keeps 256 (constant) bits more than the size of the input array. Also, the time complexity is O(1) as there are 256 chars to be checked in the input string and the duplicate will be detected at most at 256th character of the string.
I had to resolve a problem for an interview, and i didn't understood something.
The problem was, for a string like "we like hiking" to reverse the string like this: "ew ekil gnikih", this means, to explode the string like: str.split(" ") and then, each word to reverse and to add to the new string.
The problem was at the end of the problem, because was asking something about Complexity, like:
" - expected worst-case time complexity is O(N)"
- expected wors-case space complexity is O(N) (not counting the storage required for input arguments)
I resolved the problem like this:
public static String solution(String S){
if(S.length()>1 && S.length()<200000){
String toReturn = "";
String[] splitted = null;
if(S.contains(" ")){
splitted = S.split(" ");
}
else{
splitted = new String[]{S};
}
for(int i=0;i<=splitted.length-1;i++){
String wordToChange = splitted[i];
String reversedWord = "";
for(int j=wordToChange.length()-1;j>=0;j--)
reversedWord+=wordToChange.charAt(j);
toReturn+=" " + reversedWord;
}
return toReturn.trim();
}
return null;
}
What i should do about the complexity request?
Thanks!
About time complexity:
Worst-case O(N) means that you should have a worst case scenario in which you compute N iteration in order to get the result, in your case you split the String into an array of Strings [operation that probably takes O(N), but it depends on the method split()], then for each String in the array you start from the end of the String and invert the characters.
So your worst case is O(N) + O(N)... = O(N)
About space complexity:
Each String is an object and you reserve space in memory for each String you create, here you have an array of Strings, means that you create an object for each word or letter you split from the original string.
You worst case is a string like "I a m t h e o n e" in which you create a String for each character -> O(N)
Hope this clarifies your doubts
I think this is not the correct answer for at least the time complexity.
In my opinion the splitting of a String will most likely be a complexity of O(n). And it'll just add to next complexity, therefore we can forget about it.
Now you're creating an empty String and concatenating a char on top of each String. Here you're assuming this operation is as cheap like inserting a value into an array (O(1)).
In this case you should assume, that you're creating a new String each time with the member method concat(). If you're have the chance of looking into the implementation of String.concat(String) you'll see that this method is creating another char[] in the length of string1.length + string2.length and is copying both string1 and string2 into it. So this method alone has a complexity of O(n).
Because you're concatenating n chars for one word of n length, it has the complexity of O(n * (n / 2)) = O(n^2) for one word. Assuming the worst case of just one word, you have the complexity class of O(n^2).
I would concur with the space complexity class of O(n) for your code. But this is just under the assumption of the intelligence of the JVM reusing you're unused space.
Your code should work, but I think it is really not good code for this problem. You can improve it with a StringBuilder or direct operations on a char[].
The StringBuilder is a good idea, because it acts similar to a Vector or a ArrayList. I has a much bigger char[] working in its background than a String. If it's capacity is exceeded (by using the append() method), it will create a much bigger array, something like 2 time the size of the previous backing array. This might only be a time complexity of O(nlogn). But you can tweak it furthermore by initializing the StringBuilder with a capacity of the same size as your initial String. This means the backing char[] will not be copied at all, because you'll not exceed it's capacity.
Here is an example with the use of a char[]:
private static void reverseCharArray(char[] result, int start, int end) {
int halfDifference = (end - start) / 2;
for (int i = 0; i < halfDifference; i++) {
char buffer = result[start + i];
result[start + i] = result[end - i - 1];
result[end - i - 1] = buffer;
}
}
public static String reverseWordInString(String string) {
char[] stringAsCharArray = string.toCharArray();
int indexStart = 0;
for (int indexEnd = 0; indexEnd < stringAsCharArray.length; indexEnd++) {
if (stringAsCharArray[indexEnd] != ' ') {
continue;
}
StringOperations.reverseCharArray(stringAsCharArray, indexStart, indexEnd);
indexStart = indexEnd + 1;
}
StringOperations.reverseCharArray(stringAsCharArray, indexStart, stringAsCharArray.length);
return String.valueOf(stringAsCharArray);
}
This is a solution of mine for one of the problems in leetcode. Through my deductions, I concluded it to have an overall O(N^2) time complexity. However, I would like to get a confirmation on this just so that I don't continue making the same mistakes when it comes to judging the time/space complexity of an algorithm.
Oh, and the problem goes as follows:
Given an input string, reverse the string word by word.
e.g. "I am you" == "you am I"
The code is as follows:-
public String reverseWords(String s) {
//This solution is in assumption that I am restricted to a one-pass algorithm.
//This can also be done through a two-pass algorithm -- i.e. split the string and etc.
if(null == s)
return "";
//remove leading and trailing spaces
s = s.trim();
int lengthOfString = s.length();
StringBuilder sb = new StringBuilder();
//Keeps track of the number of characters that have passed.
int passedChars = 0;
int i = lengthOfString-1;
for(; i >= 0; i--){
if(s.charAt(i) == ' '){
//Appends the startOfWord and endOfWord according to passedChars.
sb.append(s.substring(i+1, (i+1+passedChars))).append(" ");
//Ignore additional space chars.
while(s.charAt(i-1) == ' '){
i--;
}
passedChars = 0;
}else{
passedChars++;
}
}
//Handle last reversed word that have been left out.
sb.append(s.substring(i+1, (i+1+passedChars)));
//return reversedString;
return sb.toString();
}
My reasoning for this being an O(N^2) algorithm:-
The loop = O(n)
StringBuilder.append = O(1)
Substring method = O(n) [as of Java 7]
On that note, if anyone else has a better solution than this, please feel free to share it! :)
I was aiming for a one-pass solution and therefore, opted out of splitting the string before the loop.
Appreciate the help!
EDIT: I meant to ask about the time complexity of the portion of the code that contains the loop. I apologize in advance if the question was misleading/confusing. The whole chunk of code is meant for clarification purposes. :)
Time complexity is O(n).
Each insertion (append(x)) to a StringBuilder is done in O(|x|), where |x| is the size of the input string you are appending. (independent of the state of the builder, on average).
Your algorithm iterates the entire string, and use String#substring() for each word in it. Since the words do not overlap, it means you do a substring() for each word one time, and append it to the builder (also once) - giving you 2|x| for each word x.
Summing it up, gives you
T(S) = |S| + sum{2|x| for each word x}
But since sum{|x| for each word x} <= |S|, this gives you total of:
T(S) = |S| + 2sum{|x| for each word x} = |S| + 2|S| = 3|S|
Since |S| is the size of the input (n), this is O(n)
Note that the important part is in jdk7, the substring() method is linear in the size of the output string, not the original one (you copy only the relevant part, not all of the string).
Here is an alternative solution which I believe may perform a little better.
public String reverseWords(String s) {
String[] array = s.split(" ");
int len = array.length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
sb.append(" ").append(array[len - i - 1]);
}
return sb.toString().trim();
}
Amit has already given you detailed explanation on the complexity computation, I would like to give you a simpler version.
In general, if we have nested loops, we consider the complexity to be O(N^2). But this is not the case always, as you have to do some activity n times for each nth part of input. E.g., if you have input of size 3, you have to do some action 3 times on each of the element. Then, you can say that your algorithm has O(n^2) complexity.
Since you are traversing and processing each part of your input string only once (even though you are using nested loops), complexity should be on the order of O(n). For proof, Amit has done quite a job.
Although, I would have used below code to reverse the order of words
String delim = " ";
String [] words = s.split(delim);
int wordCount = words.length;
for(int i = 0; i < wordCount / 2; i++) {
String temp = words[i];
words[i] = words[wordCount - i - 1];
words[wordCount - i - 1] = temp;
}
String result = Arrays.toString(words).replace(", ", delim).replaceAll("[\\[\\]]", "");
I am working on an interview question from Amazon Software
The question is "Design an algorithm to take a list of strings as well as a single input string, and return the indices of the list which are anagrams of the input string, disregarding special characters."
I was able to design the algorithm fine, what I did in psuedo code was
1.Create an array character count of the single input string
2.For each string the list, construct the an array character count
3.Compare the character count of each string in list to single output string
4.If same, add it to a list that holds all the indexes of anagrams.
5.Return that list of indices.
Here is my implementation in Java(it works, tested it)
public static List<Integer> indicesOfAnag(List<String> li, String comp){
List<Integer> allAnas = new ArrayList<Integer>();
int[] charCounts = generateCharCounts(comp);
int listLength = li.size();
for(int c=0;c<listLength; c++ ){
int[] charCountComp = generateCharCounts(li.get(c));
if(isEqualCounts(charCounts, charCountComp))
allAnas.add(c);
}
return allAnas;
}
private static boolean isEqualCounts(int[] counts1, int[] counts2){
for(int c=0;c<counts1.length;c++) {
if(counts1[c]!=counts2[c])
return false;
}
return true;
}
private static int[] generateCharCounts(String comp) {
int[] charCounts = new int[26];
int length = comp.length();
for(int c=0;c<length;c++) {
charCounts[Character.toLowerCase(comp.charAt(c)) - 'a'] ++;
}
return charCounts;
}
What I am having trouble with is analyzing the space and time complexity of this algorithm because of both of the sizes of the list and of each string. Would the time complexity algorithm just be O(N) where N is the size of the list(processing each String once) or do I have to take into account the composite complexity of the length of each String, in that case, O(N * n) where n is the length of the string? I did N * n because you ware processing n N times. And would space complexity be O(N) because I am creating N copies of the 26 length array?
And would space complexity be O(N) because I am creating N copies of the 26 length array?
Yes.
Would the time complexity algorithm just be O(N) where N is the size of the list
No. Time depends on size of input strings, it'll be O(comp.length+sum_of_li_lengths).