Can i decrease the time complexity using dynamic programming? - java

I have an array of Strings and among them one string is,
String s = "66678889966";
and I need to output it as 60078009060
i.e; I am making consecutive repeated chars as zero keeping the starting char.
Not only that string, all other strings as well, so I can't avoid outer loop to iterate over all those strings.
I solved this using a naive approach. Actually, I am solving a problem that includes another loop with an array of strings like this. Including my inner loop, it goes up to O(n^2) which is not acceptable.
BufferedReader bf = new BufferedReader(newInputStreamReader(System.in));
String s[], s1[];
s = bf.readLine().trim().split("\\s+");
s1 = bf.readLine().trim().split("\\s+");
BigInteger sb = new BigInteger(s[1]);
BigInteger sb1 = new BigInteger(s1[1]);
BigInteger indexIncre = new BigInteger("1");
BigInteger first = new BigInteger(s[1]);
BigInteger last = new BigInteger(s1[1]);
BigInteger length = last.subtract(first);
BigInteger summation = new BigInteger("0");
for (index = new BigInteger("0");
!index.subtract(length).toString().equals("1");
index =index.add(indexIncre)) {
StringBuilder str = new StringBuilder(first.toString());
int len = str.length();
char c = str.charAt(0);
for (int i = 1; i < len; i++) {
if (str.charAt(i) == c) {
str.setCharAt(i, '0');
} else
c = str.charAt(i);
}
first = first.add(indexIncre);
summation = summation.add(new BigInteger(str.toString()));
}
BigInteger modulo = BigInteger.valueOf((long) Math.pow(10, 9) + 7);
System.out.println(summation.mod(modulo));
For example
Input
1 8
2 12
output
49
This is in the form of
Input
NL L
NR R
The range of NL,L,NR,R are
1≤NL,NR≤10^5
1≤L≤R<10^100,000
Modified value is f(x)
f(8)=8,f(9)=9,f(10)=10,f(11)=10,f(12)=12
and modulo the sum of all these f(x) by 10^9+7
NL is the length of left-most string and L is the string, NR is the length of right most string and R is right most string. For example in the question length of number string 8 is 1 and the length of number string 12 is 2. The total number of strings are 8,9,10,11,12

You have a total of 'n' strings. Let's assume they are of an average 'm' length.
You will have to touch each character of each string at least once in order to know whether you have to zero out that particular character or not. Therefore the best complexity that you can achieve is O(m * n), which is quadratic.
O(m * n) is the same complexity that you would get by iterating in nested loop approach. Hence, you cannot do better than that by employing dynamic programming/memoization.

Related

Convert an array of integers (or string) of binary 1s and 0s to their alpha equivalent in Java

I have an array of integer 1s and 0s (possibly need to get converted to byte type?). I have used [an online ASCII to binary generator][1] to get the equivalent binary of this 6 digit letter sequence:
abcdef should equal 011000010110001001100011011001000110010101100110 in binary.
My array is set up as int[] curCheckArr = new int[48]; and my string is basically just using a StringBuilder to build the same ints as strings, and calling toString() - so I have access to the code as a string or an array.
I have tried a few different methods, all of which crash the browser, including:
StringBuilder curCheckAlphaSB = new StringBuilder(); // Some place to store the chars
Arrays.stream( // Create a Stream
curCheckString.split("(?<=\\G.{8})") // Splits the input string into 8-char-sections (Since a char has 8 bits = 1 byte)
).forEach(s -> // Go through each 8-char-section...
curCheckAlphaSB.append((char) Integer.parseInt(s, 2)) // ...and turn it into an int and then to a char
);
String curAlpha = curCheckAlphaSB.toString();
and
String curAlpha = "";
for (int b = 0; b < curCheckString.length()/8; b++) {
int a = Integer.parseInt(curAlpha.substring(8*b,(b+1)*8),2);
curAlpha += (char)(a);
}
How can I most efficiently convert these 48 1s and 0s to a six digit alpha character sequence?
Assuming each character is represented by precisely one byte you can iterate over the input with Integer.parseInt() (because byte value in the input is potentially unsigned):
String input = "011000010110001001100011011001000110010101100110";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.length(); i += 8) {
int c = Integer.parseInt(input.substring(i, i + 8), 2);
sb.append((char) c);
}
System.out.println(sb); // abcdef
Using a regex is probably the slowest part of these. Using a pre-compiled regex would help, but substring is faster. Not creating any Strings except the result would be faster. e.g. use StringBuilder instead of += for String.

Split Array without delimiters?

Is there a better(Faster) way to split a binary string into an Array?
My code That loops and substring every 8 characters in one element.
binary = my binary string(Huge) : "1010101011111000001111100001110110101010101"
int index = 0;
while (index < binary.length()) {
int num = binaryToInteger(binary.substring(index, Math.min(index + 8,binary.length())));
l.add( num);
temp = temp+ String.valueOf(num);
index += 8;
}
What I am trying to do is to split my binary string into pieces of 8 characters 10101010 and then get the int value of the 8 characters and will store that in arraylist witch in this case was l
My code is working but is very time consuming.. Is there a faster way of getting this done?
It's easy using regex:
binary.split("(?<=\\G.{8})");
However, it creates an array of strings. I don't get your will of creating an array of integers, since binary strings don't fit into this type (they can start with "0" and they can be really long).
I think there are mutiple options using Regex, substring, split etc available in java or Google Guavas - Splitter.fixedLength().
Splitter.fixedLength(8).split("1010101011111000001111100001110110101010101");
This Split a string, at every nth position clearly explain the performance of various functions.
It would probably faster using toCharArray:
Long time = System.currentTimeMillis();
List<Integer> l = new ArrayList<>();
int index = 0;
String binary =
"1010101011111000001111100001110110101";
char[] binaryChars = binary.toCharArray();
while (index < binaryChars.length) {
int num = 0;
for (int offset = 0; offset < Math.min(8, binary.length() - index); offset++) {
int bo = index + offset;
if (binaryChars[bo] == '1') {
num += Math.pow(2, offset + 1);
}
}
l.add(num);
index += 8;
}
System.out.println(System.currentTimeMillis() - time);
Since you want to split into groups of eight bits, I guess, you want to decode bytes rather than ints. This is easier than you might think:
String binary = "1010101011111000001111100001110110101010101";
byte[] result=new BigInteger(binary, 2).toByteArray();
Maybe you can try making a for-each loop to go through each character in the string, combine them into 8-bit values and convert into bytes. I don't know if that will be faster, just a suggestion.
for (char c : binary.toCharArray() ) { do stuff }

Fastest way to calculate all the substrings of a string and check it for a given condition

What is the fastest possible way to calculate all the possible substrings of a given string and check them for the following condition.
The condition is:
If the first and the last Character of the generated substring is same then count is incremented by one. We need to find all such possible substrings of a given very large string.
I have tried the naive brute force approach but it did not work for strings with lengths 10^7.
Please help :(
for(int c = 0 ; c < length ; c++ )
{
for( i = 3 ; i <= length - c ; i++ )
{
String sub = str.substring(c, c+i);
System.out.println(sub);
if(sub.charAt(0) == sub.charAt(sub.length()-1)){
count++;
}
}
}
Your current solution is quadratic for the size of the input string or O(n^2)
You can solve this more efficiently by counting the occurrence of each character in the string, and then counting the number of substrings that can be created with this character.
E.g. if a character occurs 4 times, then this leads to 3 + 2 + 1 = 6 substrings.
You can use the following formula for this: ((n-1) * n) / 2
This brings the complexity of the algorithm down to O(n), because for counting each character you only need to traverse the String once.
I believe this code should work:
public static void main(String[] args) {
String str = "xyzxyzxyzxyz";
Map<Character, Integer> map = new HashMap<>();
for (char c : str.toCharArray())
{
Integer count = map.get(c);
if (count == null)
count = 0;
map.put(c, count + 1);
}
int sum = 0;
for (int n : map.values())
sum += ((n - 1) * n) / 2;
System.out.println(sum);
}

Running time of permutation function

My book provides the following code for a function that computes all the permutations of a string of unique characters (see code below), and says that the running time is O(n!), "since there are n! permutations."
I don't understand how they've computed the running time as O(n!). I assume they mean "n" is the length of the original string. I think that the running time should be something like O((n + 1)XY), since the getPerms function will be called (n + 1) times, and X and Y can represent the running times of the outer and inner for loops respectively. Can someone explain to me why this is wrong / the book's answer is right?
Thanks.
public static ArrayList<String> getPerms(String str)
{
if (str == null)
return null;
ArrayList<String> permutations = new ArrayList<String>();
if (str.length() == 0)
permutations.add("");
return permutations;
char first = str.charAt(0); //first character of string
String remainder = str.substring(1); //remove first character
ArrayList<String> words = getPerms(remainder);
for (String word: words)
{
for (i = 0; i <= word.length(); i++)
{
String s = insertCharAt(word, first, i);
permutations.add(s)
}
}
return permutations;
}
public static String insertCharAt(String word, char c, int j)
{
String start = word.substring(0, i);
String end = word.substring(i);
return start + c + end;
}
Source: Cracking the Coding Interview
From our intuition, it is clear that there is no existing algorithm that generate permutation of N items that perform better than O(n!) because there are n! possibility.
You can reduce the recursive code into recurrence equation because gePerm(n) where n is a string with n length will call getPerm(n-1). Then, we use all the value returns by it and put a inner loop that loop N times. So we have
Pn = nPn-1
P1 = 1
It is easy to see that Pn = n! by telescoping the equation.
If you have hard times visualize how we come up with this equation, you can also think of this way
ArrayList<String> words = getPerms(remainder);
for (String word: words) // P(n-1)
{
for (i = 0; i <= word.length(); i++) // nP(n-1)
{
String s = insertCharAt(word, first, i);
permutations.add(s)
}
}
The count of permutations of N elements is N * (N - 1) * (N - 2) * ... * 2 * 1, i.e. N!.
First character can be any one of N characters. Next character can be one of remained N - 1 characters. Now we have N * (N - 1) possible cases already.
So, continuing we'll have N * (N - 1) * (N - 2) * ... cases at each step.
Cause the count of permutations of N elements is N!, then there isn't an implementation that can permutate an array of length N faster than N!.

How can I allocate memory for an array of digits (int) of a number with the length being equal to the number of digits of that number?

e.g. for int n = 1234 I could create a string (s.valueOf(n)), then I would define the array like this:
int[] array = new int[s.length()]; //this allocates memory for an array with 4 elements
Is there any other way to do this without using a string and only integers?
You can use Math#log10 to find the number of digits.
numOfDigits = (int)(Math.log10(n)+1);
Now you do:
int[] array = new int[numOfDigits];
Note that if n = 1999, numOfDigits will be 4. So you're allocating a memory for 4 integers and not 1999 integers.
But be careful, while reading the documentation of the method, you'll note:
If the argument is positive zero or negative zero, then the result is
negative infinity.
I assume you are talking about Java, so:
int value = myValue;
for (int noOfDigits = 1; Math.abs(value) >= 1; ++noOfDigits) {
value /= 10;
}
int[] array = new int[noOfDigits];
This does not include space for the leading sign if the number is negative, but you can easily test this condition and increment noOfDigits by one.
Use log function to find the no. of digits.
int size = (int)Math.log10(1234)+1;
int[] array = new int[size];
According to my understanding you can do the following to get count of digits
int n = 12345;
int count = 1;
while(n>10){
n = n/10;
count++;
}
int[] array = new int[count];
if (n>0){
numberOfDigets = (int)(Math.log10(n)+1);
}
else if (n < 0){
numberOfDigets = (int)(Math.log10(Math.abs(n))+1);
} else {
numberOfDigets = 1;
}
If n is bigger then zero use the Math.log10 function as Maroun Maroun wrote. If the n is less the zero use the Math.abs function to get a positiv value. If you want to allocate space for the - the add 2 instead of 1. The else clause is for the case when n is zero and sets numberOfDigets to 1.
If an extra call to a java function don't matter use this code.
if (n != 0){
numberOfDigets = (int)(Math.log10(Math.abs(n))+1);
} else {
numberOfDigets = 1;
}
Math.abs(n) will always return a positiv version of n. The value to watch out for is the Integer.min_value beacuse that value will still be negativ, but that is another question.

Categories

Resources