Find possible pairs from binary string - java

I have an array of strings with values only 0 and 1.
Example:
["100" , "110", "010", "011", "100"]
Now I want to take each string and compare it with all other strings and see how many of them differ by 1. For example:
"100" compared with "110", the element at each index is checked, if they are different by a maximum of 1 position then they are considered similar. So for String "100" in above array, the similar elements are "110" and "100".
My program returns an array of integers where each value indicates the number of similar strings at that index in list.
Example:
Input :
["100" , "110", "010", "011", "100"]
Output:
[2,3,2,1,2]
Explanation:
At index 1, input is "110" the similar strings are 3 which are "100", "010", "100".
Here is my code:
static List<Integer> process(String[] input) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < input.length; i++) {
int count = 0;
for (int j = 0; j < input.length; j++) {
if (i != j) {
int differ = 0;
for (int k = 0; k < input[i].length(); k++) {
if (input[i].charAt(k) != input[j].charAt(k)) {
differ++;
}
if (differ > 1) {
break;
}
}
if (differ <= 1) {
count++;
}
}
}
list.add(count);
}
return list;
}
How can I improve the time complexity of this code?
All Strings are of the same length and the length ranges from 1 to 20. Size of input array is up to 10,000.

We can exploit the fact that all input strings are of the same length to build a simple binary radix tree over the characters 0 and 1, and store the count of matching strings in the leaves.
You can then use a recursive function to navigate the tree for each input string, considering at most one divergence, where you visit the child with the opposite character. You sum all the leaf nodes reached in this manner.
First we build the tree:
static class Node
{
Node[] child = new Node[2];
int count;
}
static Node buildTree(String[] s)
{
Node root = new Node();
for(String bs : s)
{
Node node = root;
for(char c : bs.toCharArray())
{
int i = c - '0';
if(node.child[i] == null)
node.child[i] = new Node();
node = node.child[i];
}
node.count++;
}
return root;
}
Then we use recursion to count matching strings. Note that if we reach the leaf and diff is 0, i.e. we've located the original string, then we need to reduce the count by 1, as we're not counting self-matches.
static int countDiff(Node node, String s, int pos, int diff)
{
if(pos == s.length())
return diff == 0 ? node.count-1 : node.count;
int i = s.charAt(pos) - '0';
int sum = 0;
if(diff == 0 && node.child[(i+1)%2] != null)
sum += countDiff(node.child[(i+1)%2], s, pos+1, 1);
if(node.child[i] != null)
sum += countDiff(node.child[i], s, pos+1, diff);
return sum;
}
Test:
String[] s = {"100" , "110", "010", "011", "100"};
Node root = buildTree(s);
int[] count = new int[s.length];
for(int i=0; i<s.length; i++)
count[i] = countDiff(root, s[i], 0, 0);
System.out.println(Arrays.toString(count));
Output:
[2, 3, 2, 1, 2]

I really like RaffleBuffle's answer which uses radix trees.
But just as a faster-to-implement alternative — you might instead use int[] as a frequency map: index i stores how many copies of number i are in input.
The size of the frequency map array depends on the width of input strings — so it's up to int[220] == int[1'048'576].
static List<Integer> process(String[] input) {
var bitWidth = input[0].length();
var values = new int[input.length];
var freqMap = new int[1 << bitWidth]; // 2^bitWidth;
// fill values and frequency map
for (var i = 0; i < values.length; i++) {
var value = toInt(input[i]);
values[i] = value;
freqMap[value]++;
}
var result = new ArrayList<Integer>(values.length);
// fill results
for (var value : values) {
var numClose = freqMap[value] - 1;
// close value: one bit flipped
for (var iBit = 0; iBit < bitWidth; iBit++) {
var closeVal = flipBit(value, iBit);
numClose += freqMap[closeVal];
}
result.add(numClose);
}
return result;
}
private static int toInt(String inputValue) {
var result = 0;
for (int pos = inputValue.length() - 1, posWeight = 1; pos >= 0; pos--, posWeight <<= 1) {
if (inputValue.charAt(pos) == '1') {
result += posWeight;
}
}
return result;
}
private static int flipBit(int value, int bitPosition) {
return value ^ (1 << bitPosition);
}

A Frankenstein answer combined from:
using Set (actually HashMap to support repeating input numbers) as in talex's answer
using Integer.valueOf(String, 2) to convert input to int numbers as in GURU Shreyansh's answer
using bit fliping to compute close values as in user16394029's answer
The code we get:
static List<Integer> process(String[] input) {
var values = new ArrayList<Integer>(input.length);
var freqMap = new HashMap<Integer, Integer>(input.length);
var bitWidth = input[0].length();
for (var inputValue : input) {
var value = Integer.parseInt(inputValue, 2);
values.add(value);
freqMap.compute(value, (key, old) -> (old == null) ? 1 : (old + 1));
}
for (var i = 0; i < values.size(); i++) {
var value = values.get(i);
var numClose = freqMap.get(value) - 1;
for (var iBit = 0; iBit < bitWidth; iBit++) {
numClose += freqMap.getOrDefault(flipBit(value, iBit), 0);
}
values.set(i, numClose);
}
return values;
}
private static int flipBit(int value, int bitPosition) {
return value ^ (1 << bitPosition);
}

You can use set.
Put all strings in set s
For each string generate all string that differs by single change
Count number of generated strings that belong to s
It will have complexity of O(n*m*m) instead of O(n*n*m) your algorithm have.
n is number of strings and m is length of string.
So it is better in regards of n, but worse in regards of m.
Also it requires O(n*m) memory. Original algorithm requires O(max(log(n), log(m))) memory.
For better performance convert strings to integers.

I can suggest an O(N^2) approach using bitwise XOR. The result of XOR is 1 if the two bits are different. So, we can use this property to find the count of positions where the bit are different.
int[] num = new int[input.length];
for (int i = 0; i < input.length; i++)
num[i] = Integer.valueOf(input[i], 2); // converting to integral form
for (int i = 0; i < input.length; i++)
{
int count = 0;
for (int j = 0; j < input.length; j++)
{
if (i == j) // skip
continue;
if (Integer.bitCount(num[i] ^ num[j]) <= 1) // similar
{
++count;
}
}
list.add(count);
}
I've used bitCount method of java.lang package which returns the count of the number of one-bits in the two's complement binary representation of a number to find the set bits in the resultant XOR. Number of set bits in the resultant XOR indicates that that many positions were different between the two numbers.

Related

A method for counting unique elements in a array with loop (Java)

I'm trying to make a method that counts the number of unique elements in an array. For example, if the array contains [1,2,3,4,5,1,5] there are 3 unique elements and my method should return the number 3.
This is what I´ve got so far:
static int numberOfUniqueIntegers(int[] number, int len) {
int unique = 0;
for (int i = 0; i < len; i++){
int j;
for (j = 0; j < i; j ++) {
if (number[i] == number[j]) {
break;
}
}
if (i == j);
unique++;
}
return unique;
}
The method takes in the array number and an integer len (which is the length of the array).
But in this case: [1,2,3,4,5,1,5] my method would return 5, instead of 3. I somehow need to check if the number has been repeated before, and if not unique++.
You can create a frequency Map and then get the number of keys that have only one occurrence.
static int numberOfUniqueIntegers(int[] number) {
Map<Integer, Long> freq = Arrays.stream(number).boxed().collect(
Collectors.groupingBy(x -> x, Collectors.counting()));
return (int) freq.entrySet().stream().filter(e -> e.getValue().equals(1L))
.map(Map.Entry::getKey).count();
}
Demo
This one should work, just retain the elements already seen in a separate array :
static int numberOfUniqueIntegers(int[] number, int len) {
int unique = 0;
List<Integer> temp = new ArrayList<>();
for (int i = 0; i < len; i++){
if (!temp.contains(number[i]))
{
unique ++;
temp.add(number[i]);
}
}
return unique;
}
static int numberOfUniqueIntegers(int[] number, int len) {
int unique = 0;
boolean isRepeated;
for (int i = 0; i < len; i++){
isRepeated = false;//setting to defalut value
int j;
for (j = 0; j < len; j ++) {
if(j == i) continue;
if (number[i] == number[j]) {
isRepeated = true;//if caught duplicate
break;
}
}
//if not caught duplicate then increment unique ++
if(!isRepeated) unique++;
}
return unique;
}
What wen wrong?
You have to match every value with everyother value which you did not as because you are just using an array and there is no way to figure if futures values had a match in past. So, you have to cover the length of array and check each value against every value in it. if it was to be written using java collections then it be would much simpler and with less time complexity
If you need to do this without using any additional space, e.g. a Set, then you have no option but to compare each element in the array against all others, an O(n^2) solution.
static int numberOfUniqueIntegers(int[] number, int len)
{
int unique = 0;
for (int i = 0; i < len; i++)
{
int j = 0;
for (; j < len; j++)
if (i != j && number[i] == number[j]) break;
if (j == len) unique++;
}
return unique;
}
If you can use additional space then are options that use a Set.
You can try this with O(2n) complexity
static int numberOfUniqueIntegers() {
//int[] abc = { 1, 2, 3, 4, 5, 1, 5 };
int[] abc = { 1, 1, 1 };
Map<Integer, Integer> st = new HashMap();
int unique = 0;
for (int i = 0; i < abc.length; i++) {
if (st.isEmpty() || st.containsKey(abc[i])) {
Integer vl = st.get(abc[i]);
st.put(abc[i], Objects.nonNull(vl) ? (vl + 1) : 1);
} else {
st.put(abc[i], 1);
}
}
for (int i : st.keySet()) {
if (st.get(i) == 1) {
unique = unique + 1;
}
}
System.out.println(st);
return unique;
}
The easiest way is to just use Set.
int[] s = { 1, 2, 1, 5, 3, 4, 5, 1, 1, 5 };
int count = numberOfUniqueIntegers(s);
System.out.println("Count = " + count);
Prints
Count = 3
Set#add returns false if element exists, true otherwise
if seen doesn't contain it, add to unique.
if it has already been seen, remove from unique.
only the unique ones will remain.
static int numberOfUniqueIntegers(int[] number) {
Set<Integer> seen = new HashSet<>();
Set<Integer> unique = new HashSet<>();
for (int i : number) {
if (!seen.add(i)) {
unique.remove(i);
continue;
}
unique.add(i);
}
return unique.size();
}
Due to the nature of Set the above works in O(n) and no explicit counting of elements needs to be done.
Normally asking questions like this, many stack overflow answers will give you solutions, however, that might not be conducive to working through the problem yourself.
Keep in mind there are multiple ways to solve this kind of problem.
Use a Set (a data structure to deal specifically to deal with uniques)
Sort and count. On duplicates, move on.
Just want to point out a potential issue with your code. You are using a terminator on your conditional clause if(i == j);
Your code will compile and run correctly, however it will simply check the comparison and run count++ every time. You will want to fix this clause.
The correct syntax should be
if (i == j) {
count++
}
Check comment for the exact lines:
static int numberOfUniqueIntegers(int[] number, int len) {
int unique = 0;
for (int i = 0; i < len; i++){
int j;
for (j = 0; j < i; j ++) {
if (number[i] == number[j]) {
break;
}
}
if (i == j); // This comparison is always ignored.
unique++; // This line is always happening
}
return unique;
}
You can use set
public static int numberOfUniqueIntegers(int[] number, int len) {
Set<Integer> st = new HashSet();
int unique = 0;
for (int i = 0; i < len; i++) {
if (!st.add(number[i])) {
unique = unique - 1;
} else {
unique = unique + 1;
}
}
return unique;
}

Size of subset of an array with largest bitwise AND product?

If given an array A consisting of N integers, how can I return the size of the largest possible subset of A such that its AND product is greater than 0???
I've been at this all day and still cant get the desired result.
Are you guys able to see what's wrong with my code?
I am inputting an array of N size = {13,7,2,8,3},
output should be 3, and I get 5...
Any help is appreciated. thanks!!
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = Integer.valueOf(scan.nextLine());
int[] a = new int[n];
for(int i = 0; i < n; i++){
a[i] = Integer.valueOf(scan.nextLine());
}
int mAND = toBitWise(a);
int maxCombinados = checarMax(a, 0, 0, 0, mAND);
System.out.println(maxCombinados);
}
private static int toBitWise(int[] a){
int nAND = 0;
for(int i = 0; i < a.length; i++){
nAND &= a[i];
}
return nAND;
}
public static int checarMax(int[] a, int i, int cAND, int cSize, int mAND){
if(i == a.length){
if(cAND == mAND){
return cSize;
}
else{
return a.length;
}
}
int prueba = checarMax(a, i + 1, cAND & a[i], cSize + 1, mAND);
int ign = checarMax(a, i + 1, cAND, cSize, mAND);
return Math.max(prueba, ign);
}
}
A brute-force solution is to try all 31 permutations of non-empty subsets of that 5-value set.
To iterate the permutations, just iterate the numbers 1-31, and check the 5 bits to see which values from the array to include in the result, i.e. use bit-manipulation to find the permutations, then use bit-manipulation to AND the values of the permutation.
static int findLargestSubset(int... values) {
if (values.length > 30)
throw new IllegalArgumentException("Too many values");
// Iterate all subsets (permutations), except the empty subset
int maxSubsetSize = 0;
int subsetCount = 1 << values.length;
for (int subsetMask = 1; subsetMask < subsetCount; subsetMask++) {
// 'AND' all values in the subset
int result = -1; // all bits set
for (int i = 0; i < values.length; i++)
if ((subsetMask & (1 << i)) != 0) // value is in subset
result &= values[i];
// Check subset size if result is non-zero
if (result != 0) {
int subsetSize = Integer.bitCount(subsetMask);
if (subsetSize > maxSubsetSize)
maxSubsetSize = subsetSize;
}
}
return maxSubsetSize;
}
Test
System.out.println(findLargestSubset(13,7,2,8,3)); // prints 3
Pseudocode:
initialize ans = 0
for i in each 31 bits:
count = 0
for n each numbers in array:
check if the ith position of n has 1 -> count++
ans = max(ans, count)
return ans

I created decimal to binary method and I want to use it in 2D array but I can't

I created simple decimal to binary method like this :
public static int[] convertDectoB(int number, int size) {
int binary[] = new int[size];
for (int a = number; a > 0 && size > 0; size--, a = a / 2) {
binary[size - 1] = a % 2;
}
return binary;
}
I asked user to enter numbers like that :
"Enter numbers : "
4-2-7.
The aim is convert these 3 numbers into a binary and print it :
1 0 0
0 1 0
1 1 1
I also created numberFinder method to extract integers into the String that user entered :
public static int[] numberFinder(String numbers, int size) {
int numaraa[] = new int[size];
int a = 0;
for (int i = 0; i < numaraa.length; i++) {
for (; a < numbers.length(); a++) {
if (numbers.charAt(i) == '-')
i++;
else if (a == 0) {
if (Character.isDigit(numbers.charAt(a)))
numaraa[i] += numbers.charAt(a);
}
else {
if (Character.isDigit(numbers.charAt(a)))
numaraa[i] += numbers.charAt(a);
}
}
}
return numaraa;
}
In the end , I created 2D array and I wanna implement these binary values :
for (int i = 0; i < mainarray.length; i++) {
for (int j = 0; j < mainarray[i].length; j++) {
mainarray[i][j] = ?
}
}
}
But I can't implement all binary values for each array block.
If you need to make your own toBinaryString() method there are a lot of examples online, such as borrowing the logic from this answer Print an integer in binary format in Java:
public static String intToString(int number) {
StringBuilder result = new StringBuilder();
for(int i = 2; i >= 0 ; i--) {
int mask = 1 << i;
result.append((number & mask) != 0 ? "1" : "0");
}
return result.toString();
}
Note: I modified this method to only hold 3 digit binary codes, so the highest integer it can convert is 7. However if you want to convert larger integers, say up to 16, or 32, you'll need to increase the counter in the for-loop, which I'll leave that for you to decide.
For each line in the user input, split it on the hyphen character '-':
codes = line.split("-");
ListofCourses.addAll(Arrays.asList(codes));
...
Then call this method on your ListofCourses:
for(int r = 0; r < ListofCourses.size(); r++){
System.out.println(intToString(Integer.parseInt(ListofCourses.get(r))));
}
ListofCourses.clear();
Then should get expected output. Here's demo:
Ron's Copy
Enter 3 collections of course codes one collection per line
4-2-7
Size: 3 Sorted: [4, 2, 7]
100
010
111

How to find the longest substring with equal amount of characters efficiently

I have a string that consists of characters A,B,C and D and I am trying to calculate the length of the longest substring that has an equal amount of each one of these characters in any order.
For example ABCDB would return 4, ABCC 0 and ADDBCCBA 8.
My code currently:
public int longestSubstring(String word) {
HashMap<Integer, String> map = new HashMap<Integer, String>();
for (int i = 0; i<word.length()-3; i++) {
map.put(i, word.substring(i, i+4));
}
StringBuilder sb;
int longest = 0;
for (int i = 0; i<map.size(); i++) {
sb = new StringBuilder();
sb.append(map.get(i));
int a = 4;
while (i<map.size()-a) {
sb.append(map.get(i+a));
a+= 4;
}
String substring = sb.toString();
if (equalAmountOfCharacters(substring)) {
int length = substring.length();
if (length > longest)
longest = length;
}
}
return longest;
}
This currently works pretty well if the string length is 10^4 but I'm trying to make it 10^5. Any tips or suggestions would be appreciated.
Let's assume that cnt(c, i) is the number of occurrences of the character c in the prefix of length i.
A substring (low, high] has an equal amount of two characters a and b iff cnt(a, high) - cnt(a, low) = cnt(b, high) - cnt(b, low), or, put it another way, cnt(b, high) - cnt(a, high) = cnt(b, low) - cnt(a, low). Thus, each position is described by a value of cnt(b, i) - cnt(a, i). Now we can generalize it for more that two characters: each position is described by a tuple (cnt(a_2, i) - cnt(a_1, i), ..., cnt(a_k, i) - cnt(a_1, i)), where a_1 ... a_k is the alphabet.
We can iterate over the given string and maintain the current tuple. At each step, we should update the answer by checking the value of i - first_occurrence(current_tuple), where first_occurrence is a hash table that stores the first occurrence of each tuple seen so far. Do not forget to put a tuple of zeros to the hash map before iteration(it corresponds to an empty prefix).
If there were only A's and B's, then you could do something like this.
def longest_balanced(word):
length = 0
cumulative_difference = 0
first_index = {0: -1}
for index, letter in enumerate(word):
if letter == 'A':
cumulative_difference += 1
elif letter == 'B':
cumulative_difference -= 1
else:
raise ValueError(letter)
if cumulative_difference in first_index:
length = max(length, index - first_index[cumulative_difference])
else:
first_index[cumulative_difference] = index
return length
Life is more complicated with all four letters, but the idea is much the same. Instead of keeping just one cumulative difference, for A's versus B's, we keep three, for A's versus B's, A's versus C's, and A's versus D's.
Well, first of all abstain from constructing any strings.
If you don't produce any (or nearly no) garbage, there's no need to collect it, which is a major plus.
Next, use a different data-structure:
I suggest 4 byte-arrays, storing the count of their respective symbol in the 4-span starting at the corresponding string-index.
That should speed it up considerably.
You can count the occurrences of the characters in word. Then, a possible solution could be:
If min is the minimum number of occurrences of any character in word, then min is also the maximum possible number of occurrences of each character in the substring we are looking for. In the code below, min is maxCount.
We iterate over decreasing values of maxCount. At every step, the string we are searching for will have length maxCount * alphabetSize. We can view this as the size of a sliding window we can slide over word.
We slide the window over word, counting the occurrences of the characters in the window. If the window is the substring we are searching for, we return the result. Otherwise, we keep searching.
[FIXED] The code:
private static final int ALPHABET_SIZE = 4;
public int longestSubstring(String word) {
// count
int[] count = new int[ALPHABET_SIZE];
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
count[c - 'A']++;
}
int maxCount = word.length();
for (int i = 0; i < count.length; i++) {
int cnt = count[i];
if (cnt < maxCount) {
maxCount = cnt;
}
}
// iterate over maxCount until found
boolean found = false;
while (maxCount > 0 && !found) {
int substringLength = maxCount * ALPHABET_SIZE;
found = findSubstring(substringLength, word, maxCount);
if (!found) {
maxCount--;
}
}
return found ? maxCount * ALPHABET_SIZE : 0;
}
private boolean findSubstring(int length, String word, int maxCount) {
int startIndex = 0;
boolean found = false;
while (startIndex + length <= word.length()) {
int[] count = new int[ALPHABET_SIZE];
for (int i = startIndex; i < startIndex + length; i++) {
char c = word.charAt(i);
int cnt = ++count[c - 'A'];
if (cnt > maxCount) {
break;
}
}
if (equalValues(count, maxCount)) {
found = true;
break;
} else {
startIndex++;
}
}
return found;
}
// Returns true if all values in c are equal to value
private boolean equalValues(int[] count, int value) {
boolean result = true;
for (int i : count) {
if (i != value) {
result = false;
break;
}
}
return result;
}
[MERGED] This is Hollis Waite's solution using cumulative counts, but taking my observations at points 1. and 2. into consideration. This may improve performance for some inputs:
private static final int ALPHABET_SIZE = 4;
public int longestSubstring(String word) {
// count
int[][] cumulativeCount = new int[ALPHABET_SIZE][];
for (int i = 0; i < ALPHABET_SIZE; i++) {
cumulativeCount[i] = new int[word.length() + 1];
}
int[] count = new int[ALPHABET_SIZE];
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
count[c - 'A']++;
for (int j = 0; j < ALPHABET_SIZE; j++) {
cumulativeCount[j][i + 1] = count[j];
}
}
int maxCount = word.length();
for (int i = 0; i < count.length; i++) {
int cnt = count[i];
if (cnt < maxCount) {
maxCount = cnt;
}
}
// iterate over maxCount until found
boolean found = false;
while (maxCount > 0 && !found) {
int substringLength = maxCount * ALPHABET_SIZE;
found = findSubstring(substringLength, word, maxCount, cumulativeCount);
if (!found) {
maxCount--;
}
}
return found ? maxCount * ALPHABET_SIZE : 0;
}
private boolean findSubstring(int length, String word, int maxCount, int[][] cumulativeCount) {
int startIndex = 0;
int endIndex = (startIndex + length) - 1;
boolean found = true;
while (endIndex < word.length()) {
for (int i = 0; i < ALPHABET_SIZE; i++) {
if (cumulativeCount[i][endIndex] - cumulativeCount[i][startIndex] != maxCount) {
found = false;
break;
}
}
if (found) {
break;
} else {
startIndex++;
endIndex++;
}
}
return found;
}
You'll probably want to cache cumulative counts of characters for each index of String -- that's where the real bottleneck is. Haven't thoroughly tested but something like the below should work.
public class Test {
static final int LEN = 4;
static class RandomCharSequence implements CharSequence {
private final Random mRandom = new Random();
private final int mAlphabetLen;
private final int mLen;
private final int mOffset;
RandomCharSequence(int pLen, int pOffset, int pAlphabetLen) {
mAlphabetLen = pAlphabetLen;
mLen = pLen;
mOffset = pOffset;
}
public int length() {return mLen;}
public char charAt(int pIdx) {
mRandom.setSeed(mOffset + pIdx);
return (char) (
'A' +
(mRandom.nextInt() % mAlphabetLen + mAlphabetLen) % mAlphabetLen
);
}
public CharSequence subSequence(int pStart, int pEnd) {
return new RandomCharSequence(pEnd - pStart, pStart, mAlphabetLen);
}
#Override public String toString() {
return (new StringBuilder(this)).toString();
}
}
public static void main(String[] pArgs) {
Stream.of("ABCDB", "ABCC", "ADDBCCBA", "DADDBCCBA").forEach(
pWord -> System.out.println(longestSubstring(pWord))
);
for (int i = 0; ; i++) {
final double len = Math.pow(10, i);
if (len >= Integer.MAX_VALUE) break;
System.out.println("Str len 10^" + i);
for (int alphabetLen = 1; alphabetLen <= LEN; alphabetLen++) {
final Instant start = Instant.now();
final int val = longestSubstring(
new RandomCharSequence((int) len, 0, alphabetLen)
);
System.out.println(
String.format(
" alphabet len %d; result %08d; time %s",
alphabetLen,
val,
formatMillis(ChronoUnit.MILLIS.between(start, Instant.now()))
)
);
}
}
}
static String formatMillis(long millis) {
return String.format(
"%d:%02d:%02d.%03d",
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)),
TimeUnit.MILLISECONDS.toMillis(millis) -
TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(millis))
);
}
static int longestSubstring(CharSequence pWord) {
// create array that stores cumulative char counts at each index of string
// idx 0 = char (A-D); idx 1 = offset
final int[][] cumulativeCnts = new int[LEN][];
for (int i = 0; i < LEN; i++) {
cumulativeCnts[i] = new int[pWord.length() + 1];
}
final int[] cumulativeCnt = new int[LEN];
for (int i = 0; i < pWord.length(); i++) {
cumulativeCnt[pWord.charAt(i) - 'A']++;
for (int j = 0; j < LEN; j++) {
cumulativeCnts[j][i + 1] = cumulativeCnt[j];
}
}
final int maxResult = Arrays.stream(cumulativeCnt).min().orElse(0) * LEN;
if (maxResult == 0) return 0;
int result = 0;
for (int initialOffset = 0; initialOffset < LEN; initialOffset++) {
for (
int start = initialOffset;
start < pWord.length() - result;
start += LEN
) {
endLoop:
for (
int end = start + result + LEN;
end <= pWord.length() && end - start <= maxResult;
end += LEN
) {
final int substrLen = end - start;
final int expectedCharCnt = substrLen / LEN;
for (int i = 0; i < LEN; i++) {
if (
cumulativeCnts[i][end] - cumulativeCnts[i][start] !=
expectedCharCnt
) {
continue endLoop;
}
}
if (substrLen > result) result = substrLen;
}
}
}
return result;
}
}
Suppose there are K possible letters in a string of length N. We could track the balance of letters seen with a vector pos of length K that is updated as follows:
If letter 1 is seen, add (K-1, -1, -1, ...)
If letter 2 is seen, add (-1, K-1, -1, ...)
If letter 3 is seen, add (-1, -1, K-1, ...)
Maintain a hash that maps pos to the first string position where pos is reached. Balanced substrings occur whenever hash[pos] already exists and the substring value is s[hash[pos]:pos].
The cost of maintaining the hash is O(log N) so processing the string takes O(N log N). How does this compare with solutions so far? These types of problems tend to have linear solutions but I haven't come across one yet.
Here's some code demonstrating the idea for 3 letters and a run using biased random strings. (Uniform random strings allow for solutions that are around half the string length, which is unwieldy to print).
#!/usr/bin/python
import random
from time import time
alphabet = "abc"
DIM = len(alphabet)
def random_string(n):
# return a random string over choices[] of length n
# distribution of letters is non-uniform to make matches harder to find
choices = "aabbc"
s = ''
for i in range(n):
r = random.randint(0, len(choices) - 1)
s += choices[r]
return s
def validate(s):
# verify frequencies of each letter are the same
f = [0, 0, 0]
a2f = {alphabet[i] : i for i in range(DIM)}
for c in s:
f[a2f[c]] += 1
assert f[0] == f[1] and f[1] == f[2]
def longest_balanced(s):
"""return length of longest substring of s containing equal
populations of each letter in alphabet"""
slen = len(s)
p = [0 for i in range(DIM)]
vec = {alphabet[0] : [2, -1, -1],
alphabet[1] : [-1, 2, -1],
alphabet[2] : [-1, -1, 2]}
x = -1
best = -1
hist = {str([0, 0, 0]) : -1}
for c in s:
x += 1
p = [p[i] + vec[c][i] for i in range(DIM)]
pkey = str(p)
if pkey not in hist:
hist[pkey] = x
else:
span = x - hist[pkey]
assert span % DIM == 0
if span > best:
best = span
cand = s[hist[pkey] + 1: x + 1]
print("best so far %d = [%d,%d]: %s" % (best,
hist[pkey] + 1,
x + 1,
cand))
validate(cand)
return best if best > -1 else 0
def main():
#print longest_balanced( "aaabcabcbbcc" )
t0 = time()
s = random_string(1000000)
print "generate time:", time() - t0
t1 = time()
best = longest_balanced( s )
print "best:", best
print "elapsed:", time() - t1
main()
Sample run on an input of 10^6 letters with an alphabet of 3 letters:
$ ./bal.py
...
best so far 189 = [847894,848083]: aacacbcbabbbcabaabbbaabbbaaaacbcaaaccccbcbcbababaabbccccbbabbacabbbbbcaacacccbbaacbabcbccaabaccabbbbbababbacbaaaacabcbabcbccbabbccaccaabbcabaabccccaacccccbaacaaaccbbcbcabcbcacaabccbacccacca
best: 189
elapsed: 1.43609690666

How can I cut a section of an array in java

I have to write a method in java, where having in input an array "a" of numbers and a number "x" returns an array of elements which follows the last occorence of "x " in "a". For example
with input {0,1,2,3,4,5,6,7,8,9} and x=6 the method must return {7,8,9} meanwhile with {4,1,4,2} and x =4 the method must return {2} and if the x is not in "a" then it must return empty array {} (or array with 0 length)
so far we haven't study classes or objects .here an example of another program we made so far
boolean arrayIncluso( int[] s,int[] t ) {
boolean flag=true;
for(int i=0;i< s.length;i++){
int c1 = 0 ;
int c2 = 0 ;
for(int j=0;j< s.length;j++){
if(s[i] == s[j]){
c1 ++;
}
}
for(int j=0;j< t.length;j++){
if(s[i] == t[j]){
c2 ++;
}
}
if(c1 > c2)
flag= false;
}
return flag;
}
can someone explain to me
why this
t[i-idx-1 ] = s[i];
instead of this
for(int j=0;j<t.length;j++){
t[j]=a[i];
}
return t;
You can split the problem into two parts:
Find last index of the character x. This can be done using an easy for loop.
int idx = -1;
for (int i = 0; i < s.length; i++) {
if (s[i] == x) idx = i;
}
After you found this index, create a new array starting from this element. It can be done with a second (not nested) for loop, or you can use Arrays.copyOfRange()
//make sure idx != -1
int[] t = new int[s.length - idx - 1];
for (int i = idx+1; i < s.length; i++)
t[i-idx-1 ] = s[i];
System.out.println(Arrays.toString(t)); //print to make sure it's all fine
OR
t = Arrays.copyOfRange(s, idx+1, s.length);
System.out.println(Arrays.toString(t));
Here's a general algorithm: (you have to code it yourself)
Run through the array a keeping track of the current index of the number x. Call this index e.g.lastOccurance.
Return the array from lastOccurance + 1 and onwards.
Don't forget checks for no occurances and if the last occurance is end of array.
Using lists:
int pos = a.lastIndexOf(x);
List<Integer> result = null;
if(pos > -1)
result = a.subList(pos+1, a.size());
You can build your the list from an arra y using Arrays:
Integer [] array = new Integer[3];
...
...
List<Integer> a = Arrays.asList(array);
Alltogether would result in a code like:
List<Integer> a = Arrays.asList(array);
int pos = a.lastIndexOf(x);
int [] result = null;
if(pos > -1) {
List<Integer> sl = a.subList(pos+1, a.size());
result = new int[sl.size()];
for(int i = 0; i < sl.size(); i++)
result[i] = sl.get(i);
}
You can use commons lang library in order to determine the last index of an array element with method ArrayUtils.lastIndexOf, that way it turns to be a one-liner:
int[] t = Arrays.copyOfRange(s, ArrayUtils.lastIndexOf(s, x) + 1, s.length);

Categories