You are given an infinite stream of words (no spaces), each word also has an attached timestamp, starting at 0 and its in the format like 0, 1, 2, 3, 4, 5, ... , 6. We have APIs:
public class StreamClass {
public void consumeNextString(String next, int timeStamp);
public String getStrings(); // joins all strings into space seprated string using the below constraint
}
You are to implement both these functions. getStrings, specifically has the behavior that if you say had a stream like
one : 4
the: 5
hello : 12
the : 14
menlo: 15
If you got called getStrings now, it should print one hello the menlo instead of one the hello the menlo since the is duplicated at timestamp 11, 14 (current timestamp is 15). The oldest the at timestamp 5 got disregarded.
Later on, after the stream looks like:
one : 4
the: 5
hello : 12
the : 14
menlo: 15
big: 123
getStrings should print one the hello the menlo big because there are no duplicates in the last 10 second window (current timestamp is 123)
Work: I am thinking of an optimal way to do this, this is from an interview question.
The problem is, I dont see any good way of doing this other than just brute force, ie, storing every string then manually looking at the 10 second window to take out the oldest string, but surely there must be SOMETHING more optimal?
Well, here is a possible solution.
I used two Lists to hold the words and their timestamps.
The field lastTimeStamp is updated as each entry is consumed. It is used
to maintain the local window of seconds
when the last windows of time is entered, I simply iterate over the list of words removing the oldest duplicates.
after getString() is called, all lists are cleared to start the process anew.
This works for the supplied data and other data I have tested.
public class SlidingWindow10seconds {
public static void main(String[] args) {
StreamClass sc = new StreamClass();
sc.consumeNextString("one", 4);
sc.consumeNextString("the", 5);
sc.consumeNextString("hello", 12);
sc.consumeNextString("the", 14);
sc.consumeNextString("menlo", 15);
System.out.println(sc.getStrings());
sc.consumeNextString("one", 4);
sc.consumeNextString("the", 5);
sc.consumeNextString("hello", 12);
sc.consumeNextString("the", 14);
sc.consumeNextString("menlo", 15);
sc.consumeNextString("big", 123);
System.out.println(sc.getStrings());
}
Prints
one the hello menlo
one the hello the menlo big
class StreamClass {
int lastTimeStamp = 0;
final int windowSize = 10;
List<Integer> timeStamps = new ArrayList<>();
List<String> words = new ArrayList<>();
public void consumeNextString(String next, int timeStamp) {
words.add(next);
timeStamps.add(timeStamp);
lastTimeStamp = timeStamp;
}
public String getStrings() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < words.size(); i++) {
int ts = timeStamps.get(i);
// append all words if outside the window
if (ts < lastTimeStamp - windowSize) {
sb.append(words.get(i) + " ");
} else {
// Now iterate thru the list removing the oldest
// duplicates by adding in reverse order
Set<String> distinct = new LinkedHashSet<>();
for (int k = words.size()-1; k >= i; k--) {
distinct.add(words.get(k));
}
// and now reverse that using stack.
Stack<String> stk = new Stack<>();
stk.addAll(distinct);
while(!stk.isEmpty()) {
sb.append(stk.pop()+" ");
}
break;
}
}
sb.setLength(sb.length()-1);
words.clear();
timeStamps.clear();
return sb.toString();
}
}
Related
I have the simple task to print elements inside a LinkedList that has no duplicates with commas in between, but I have come up with two ways to do it and I'm unsure about how LinkedList iteration works so I don't know which way is best. I have some assumptions about both ways.
public static void main(String[] args) {
LinkedList<Integer> pres = new LinkedList<Integer>();
pres.add(112);
pres.add(114);
pres.add(326);
pres.add(433);
pres.add(119);
// ---------------------------------- METHOD 1 ---------------------------------
for (int i = 0; i < pres.size(); i++) {
System.out.print(pres.get(i) + (i == pres.size() - 1 ? "" : ","));
}
// ---------------------------------- METHOD 2 ---------------------------------
for (int courseIndex : pres) {
System.out.print(courseIndex + (courseIndex == pres.getLast() ? "" : ","));
}
}
For Method 1, I'm wondering if calling pres.get(i) in every iteration traverses the list from the beginning each time: (112) - (112 -> 114) - (112 -> 114 -> 326)...
Or does the pointer stay where it last was and just move to the next element?
For Method 2, it seems like the foreach loop avoids the possible problem that I'm assuming in Method 1, but I'm calling getLast on every iteration as well. Is it a doubly linked list? Is get last an O(1) operation? If not, is calling getLast on each iteration even worse than Method 1, since it traverses the list all the way down each time?
String s = pres.stream()
.map(Integer::toString)
.collect(Collectors.joining(", "));
By the way:
List<Integer> pres = new LinkedList<>();
Collections.addAll(pres,
112, 114, 326, 433, 119);
or
List<Integer> pres = List.of(112, 114, 326, 433, 119);
You then later might change to an other implementation:
List<Integer> pres = new ArrayList<>();
In a linked list, get-by-index (get(i)) is slow and can require iterating through much of the list if the index isn't near the ends. You ask if it just moves "the pointer", but there is no such pointer. Method 1 is therefore a bad idea.
Method 2 is the right idea. The for(var: list) will use an iterator, which is exactly the kind of pointer you are talking about.
Checking the last element is bad form, though, even though it is a double-linked list. You should do it something like this, instead of writing code that behaves oddly if the no-duplicates rule is broken:
String sep = "";
for (int courseIndex : pres) {
System.out.print(sep + courseIndex);
sep = ",";
}
System.out.println();
Actually, I/O calls should usually be considered slow, so it's best to make just one per line:
StringBuilder buf = new StringBuilder();
for (int courseIndex : pres) {
if (buf.length() > 0) {
buf.append(",");
}
buf.append(courseIndex);
}
System.out.println(buf.toString());
What type of collection structure would be best suited for below structure .
I want to create a rows of start and end positions and wanted to store it as a structure. based on the number of occurrences I want to execute a function .
Example
StartPosition Endposition
1 5
6 9
10 14
15 18
Now I want to store the the values like ( (1,5) , (6,9) , (10,14) , (15,18) )
Now I want to execute a function 4 times as I have 4 occurrences
for (i=1 , i <4 , i ++)
{
f(xyz , Startposition , endposition)
}
I suggest you use create a simple class and a List<> of this class.
public class Range{
private int start;
private int end;
// constructor
// gets and setts
}
And you would use like this:
List<Range> ranges = new ArrayList<>();
ranges.add(new Range(1,5));
ranges.add(new Range(6,9));
...
ranges.add(new Range(x,y));
for(Range range : ranges){
f(xyz, range.getStart(), range.getEnd());
}
As I understand, two dimensional array would do. Do you have any reason why you want a collection here?
Option 1: use Array. This is very simple.
int[] arr = { 1, 5, 6, 9, 10, 14, 15, 18 };
for (int i = 0; i < arr.length; i += 2) {
int start = arr[i];
int end = arr[i + 1];
// payload
}
Option 2: use TreeSet with custom comparator. A little bit more difficult, but code is more clear.
Set<Pair<Integer, Integer>> ranges = new TreeSet<>(Comparator.comparingInt((ToIntFunction<Pair<Integer, Integer>>)Pair::getKey).thenComparingInt(Pair::getValue));
ranges.add(new Pair<>(1, 5));
ranges.add(new Pair<>(6, 9));
ranges.add(new Pair<>(10, 14));
ranges.add(new Pair<>(15, 18));
for (Pair<Integer, Integer> range : ranges) {
int start = range.getKey();
int end = range.getValue();
// payload
}
You can use built in java.awt.Point class to set X,Y values. Stores those objects in either java.util.ArrayList or java.util.LinkedList
So given a string such as: 0100101, I want to return a random single index of one of the positions of a 1 (1, 5, 6).
So far I'm using:
protected int getRandomBirthIndex(String s) {
ArrayList<Integer> birthIndicies = new ArrayList<Integer>();
for (int i = 0; i < s.length(); i++) {
if ((s.charAt(i) == '1')) {
birthIndicies.add(i);
}
}
return birthIndicies.get(Randomizer.nextInt(birthIndicies.size()));
}
However, it's causing a bottle-neck on my code (45% of CPU time is in this method), as the strings are over 4000 characters long. Can anyone think of a more efficient way to do this?
If you're interested in a single index of one of the positions with 1, and assuming there is at least one 1 in your input, you can just do this:
String input = "0100101";
final int n=input.length();
Random generator = new Random();
char c=0;
int i=0;
do{
i = generator.nextInt(n);
c=input.charAt(i);
}while(c!='1');
System.out.println(i);
This solution is fast and does not consume much memory, for example when 1 and 0 are distributed uniformly. As highlighted by #paxdiablo it can perform poorly in some cases, for example when 1 are scarce.
You could use String.indexOf(int) to find each 1 (instead of iterating every character). I would also prefer to program to the List interface and to use the diamond operator <>. Something like,
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Finally, if you need to do this many times, save the List as a field and re-use it (instead of calculating the indices every time). For example with memoization,
private static Random rand = new Random();
private static Map<String, List<Integer>> memo = new HashMap<>();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies;
if (!memo.containsKey(s)) {
birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
memo.put(s, birthIndicies);
} else {
birthIndicies = memo.get(s);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Well, one way would be to remove the creation of the list each time, by caching the list based on the string itself, assuming the strings are used more often than they're changed. If they're not, then caching methods won't help.
The caching method involves, rather than having just a string, have an object consisting of:
current string;
cached string; and
list based on the cached string.
You can provide a function to the clients to create such an object from a given string and it would set the string and the cached string to whatever was passed in, then calculate the list. Another function would be used to change the current string to something else.
The getRandomBirthIndex() function then receives this structure (rather than the string) and follows the rule set:
if the current and cached strings are different, set the cached string to be the same as the current string, then recalculate the list based on that.
in any case, return a random element from the list.
That way, if the list changes rarely, you avoid the expensive recalculation where it's not necessary.
In pseudo-code, something like this should suffice:
# Constructs fastie from string.
# Sets cached string to something other than
# that passed in (lazy list creation).
def fastie.constructor(string s):
me.current = s
me.cached = s + "!"
# Changes current string in fastie. No list update in
# case you change it again before needing an element.
def fastie.changeString(string s):
me.current = s
# Get a random index, will recalculate list first but
# only if necessary. Empty list returns index of -1.
def fastie.getRandomBirthIndex()
me.recalcListFromCached()
if me.list.size() == 0:
return -1
return me.list[random(me.list.size())]
# Recalculates the list from the current string.
# Done on an as-needed basis.
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
for idx = 0 to me.cached.length() - 1 inclusive:
if me.cached[idx] == '1':
me.list.append(idx)
You also have the option of speeding up the actual searching for the 1 character by, for example, useing indexOf() to locate them using the underlying Java libraries rather than checking each character individually in your own code (again, pseudo-code):
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
idx = me.cached.indexOf('1')
while idx != -1:
me.list.append(idx)
idx = me.cached.indexOf('1', idx + 1)
This method can be used even if you don't cache the values. It's likely to be faster using Java's probably-optimised string search code than doing it yourself.
However, you should keep in mind that your supposed problem of spending 45% of time in that code may not be an issue at all. It's not so much the proportion of time spent there as it is the absolute amount of time.
By that, I mean it probably makes no difference what percentage of the time being spent in that function if it finishes in 0.001 seconds (and you're not wanting to process thousands of strings per second). You should only really become concerned if the effects become noticeable to the user of your software somehow. Otherwise, optimisation is pretty much wasted effort.
You can even try this with best case complexity O(1) and in worst case it might go to O(n) or purely worst case can be infinity as it purely depends on Randomizer function that you are using.
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
If your Strings are very long and you're sure it contains a lot of 1s (or the String you're looking for), its probably faster to randomly "poke around" in the String until you find what you are looking for. So you save the time iterating the String:
String s = "0100101";
int index = ThreadLocalRandom.current().nextInt(s.length());
while(s.charAt(index) != '1') {
System.out.println("got not a 1, trying again");
index = ThreadLocalRandom.current().nextInt(s.length());
}
System.out.println("found: " + index + " - " + s.charAt(index));
I'm not sure about the statistics, but it rare cases might happen that this Solution take much longer that the iterating solution. On case is a long String with only a very few occurrences of the search string.
If the Source-String doesn't contain the search String at all, this code will run forever!
One possibility is to use a short-circuited Fisher-Yates style shuffle. Create an array of the indices and start shuffling it. As soon as the next shuffled element points to a one, return that index. If you find you've iterated through indices without finding a one, then this string contains only zeros so return -1.
If the length of the strings is always the same, the array indices can be static as shown below, and doesn't need reinitializing on new invocations. If not, you'll have to move the declaration of indices into the method and initialize it each time with the correct index set. The code below was written for strings of length 7, such as your example of 0100101.
// delete this and uncomment below if string lengths vary
private static int[] indices = { 0, 1, 2, 3, 4, 5, 6 };
protected int getRandomBirthIndex(String s) {
int tmp;
/*
* int[] indices = new int[s.length()];
* for (int i = 0; i < s.length(); ++i) indices[i] = i;
*/
for (int i = 0; i < s.length(); i++) {
int j = randomizer.nextInt(indices.length - i) + i;
if (j != i) { // swap to shuffle
tmp = indices[i];
indices[i] = indices[j];
indices[j] = tmp;
}
if ((s.charAt(indices[i]) == '1')) {
return indices[i];
}
}
return -1;
}
This approach terminates quickly if 1's are dense, guarantees termination after s.length() iterations even if there aren't any 1's, and the locations returned are uniform across the set of 1's.
I know that if you have two HashSet the you can create a third one adding the two.However, for my purpose I need to change my previous HashSet, look for certain condition , and then if not met then change the set again.My purpose is that that I will give an input, say number 456, and look for digits(1 through 9, including 0).If I'm unable to find size 10 for the HashSet then I will multiply the number with 2 , and do the same.So I'll get 912; the size is 6 now(and I need to get all digits 1-9 & 0, i.e., size 10).Now I will multiply it by 3 and I get 2736 , the size is now 7.I keep doing so until I get size 10.At the time I get size 10, I will complete the loop and return the last number that concluded the loop, following the incremental multiplication rule.My approach is as follows.It has errors so won't run but it represents my understanding as of now.
public long digitProcessSystem(long N) {
// changing the passed in number into String
String number = Long.toString(N);
//splitting the String so that I can investigate each digit
String[] arr = number.split("");
// Storing the digits(which are Strings now) into HashSet
Set<String> input = new HashSet<>(Arrays.asList(arr));
// Count starts for incremental purpose later.
count =1;
//When I get all digits; 1-9, & 0, I need to return the last number that concluded the condition
while (input.size() == 10) {
return N;
}
// The compiler telling me to delete the else but as a new Java user so far my understanding is that I can use `else` with `while`loops.Correct me if I'm missing something.
else {
// Increment starts following the rule; N*1, N*2,N*3,...till size is 10
N = N*count;
// doing everything over
String numberN = Long.toString(N);
String[] arr1 = number.split("");
// need to change the previous `input`so that the new updated `HashSet` gets passed in the while loop to look for size 10.This is error because I'm using same name `input`. But I don't want to create a new `set` , I need to update the previous `set` which I don't know how.
Set<String> input = new HashSet<>(Arrays.asList(arr1));
// increments count
count++;
}
clear() input and add the new values. Something like
// Set<String> input = new HashSet<>(Arrays.asList(arr1));
input.clear();
input.addAll(Arrays.asList(arr1));
and
while (input.size() == 10) {
should be
if (input.size() == 10) {
Or your else isn't tied to an if.
I have a puzzle that I solved. I will try to briefly describe it and then ask question.
Map phone numbers to text. For example:
101 -> 101, but 501 -> J01, K01, L01 (AFAIK).
We get 9digit number and need to produce all combination, even those that are not grammaticaly correct.
I created a program that basically grows like a tree. When a number that is not 0 or 1 is found we create a new branch with already translated text + one of possible letters + rest of number.
I solved it by creating a new thread every time new translateable digit is encountered.
Do you think it is a bad practice? Can it be solved better?
Thank you.
Creating a thread for each possibility seems like overkill, what you really want is recursion. Try this:
String [] lookup = { "0", "1", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ" };
List<String> results = new ArrayList<String>();
void translatePhone(String phoneNumber, int position) {
int index = Integer.parseInt(phoneNumber.substring(position, position + 1));
for (int i = 0; i < lookup[index].length; i++) {
String xlated = phoneNumber.substring(0, position) + lookup[index].charAt(i) + phoneNumber.substring(position + 1);
if (position + 1 == phoneNumber.length) {
results.add(xlated);
} else {
translatePhone(xlated, position + 1);
}
}
}
Call translatePhone(phoneString, 0) to start it off. Once that returns results should have all your results.