java BigDecimal result not the desired one - java

So, I'm trying to write a program that calculates an expression such as "3+(6/5)+(2*3/7)7/2(9/4+4*1)=", where the order of precedence is as it is written. The only thing that matters is that if there is a sub-expression present within brackets, they should be calculated first.
My code is okay to some extent but the final result is not what it's desired. For example "3+(6/5)+(2*3/7)7/2(9/4+4*1)=" should print 110.63 but it prints 110.69.
"4+6/5+(4*9-8)/7*2=" prints 8.58 but should be 8.57.
I'm almost certain that it has something to do with the rounding but I'm not really sure and can't think of a solution since I'm fairly new to BigDecimal or even programming in general.
PS: It works fine with doubles but I'm receiving runtime errors while running unknown tests on a certain platform for testing, so I thought I'd try with BigDecimal in case it's because of too many numbers after the decimal point but oh, well..
Heres my code:
String nc = scan.nextLine();
BigDecimal j = new BigDecimal(0);
for (int i = 0; i < nc.length(); i++) {
if (nc.charAt(i) == '(') {
int startIndex = nc.indexOf("(");
int endIndex = nc.indexOf(")");
String toBeReplaced = nc.substring(startIndex, endIndex + 1);
String omg = toBeReplaced.replaceAll("\\(|\\)", "");
BigDecimal ok = new BigDecimal(0);
int m=0;
String split[]=omg.split("\\+|\\-|\\*|\\/|\\=");
for (int k = 0; k < omg.length(); k++) {
if (k == 0) {
ok = new BigDecimal(split[0]);
}
if (omg.charAt(k) == '+') {
m++;
ok=ok.add(new BigDecimal(split[m]));
} else if (omg.charAt(k) == '-') {
m++;
ok = ok.subtract(new BigDecimal(split[m]));
} else if (omg.charAt(k) == '*') {
m++;
ok = ok.multiply(new BigDecimal(split[m]));
} else if (omg.charAt(k) == '/') {
m++;
ok = ok.divide(new BigDecimal(split[m]),2,RoundingMode.HALF_EVEN);
}
}
nc = nc.replace(toBeReplaced, ok + "");
}
}
String split[]=nc.split("\\+|\\-|\\*|\\/|\\=");
int k = 0;
for (int i = 0; i < nc.length(); i++) {
if (i == 0) {
j = new BigDecimal(split[0]);
}
if (nc.charAt(i) == '+') {
k++;
j = j.add(new BigDecimal(split[k]));
} else if (nc.charAt(i) == '-') {
k++;
j = j.subtract(new BigDecimal(split[k]));
} else if (nc.charAt(i) == '*') {
k++;
j = j.multiply(new BigDecimal(split[k]));
} else if (nc.charAt(i) == '/') {
k++;
j = j.divide(new BigDecimal(split[k]),2,RoundingMode.HALF_EVEN);
}
}
System.out.println(j.setScale(2, BigDecimal.ROUND_HALF_EVEN);

Related

How to continue 2 loops at the same time? [duplicate]

This question already has answers here:
Continue at first loop , inside the second loop
(7 answers)
Closed 1 year ago.
For this code kata I need to continue 2 for loops at the same time. How can I do that?
public class StringMerger {
public static boolean isMerge(String s, String part1, String part2) {
StringBuilder MergedWord = new StringBuilder("");
String Whole = part1+part2;
for(int j = 0; j < Whole.length(); j++){
for(int I = 0; I < part1.length(); I++){
if((Character.compare(s.charAt(j), part1.charAt(I)) == 0) && (j == I)){
MergedWord.append(s.charAt(I)+"");
continue;
}
else
break;
}
for(int i = 0; i < part2.length(); i++){
if((Character.compare(s.charAt(j), part2.charAt(i)) == 0) && (j == i)){
MergedWord.append(part2.charAt(i) + "");
continue;
}
else
break;
}
}
return s.equals(MergedWord.toString())? true : false;
}
}
I noticed when one of the for loops continue it only continues the internal loop, but labels would continue the upper loop and would be inefficient. Could I continue 2 for loops at the same time a.k.a continue the inner and upper loop in this nested for loop?
The task is to match each character in the target string, so you only need one loop, which iterates over the characters in the string. You do need two other loop variables to track how much of the two source strings have been used up.
My solution below is looping over three things at once: the target string and the two 'part' strings. The rate it moves over the 'part' strings isn't constant, but it does progress over them monotonically.
It wasn't clear to me whether the source strings could include extra characters not used in the target string. As the example didn't show any, I assumed not.
public class MergedStringChecker {
public static void main(String[] args) {
String target = "codewars";
String part1 = "cdw";
String part2 = "oears";
for (int i = 0, p1 = 0, p2 = 0; i < target.length(); ++i) {
if (p1 < part1.length() && target.charAt(i) == part1.charAt(p1)) {
++p1;
} else if (p2 < part2.length() && target.charAt(i) == part2.charAt(p2)) {
++p2;
} else {
throw new RuntimeException("No matching characters at index " + i);
}
}
}
}
The above solution is not Unicode safe if the string contains > 16 bit code points.
Try this.
public static boolean isMerge(String s, String part1, String part2) {
int length = s.length();
int length1 = part1.length();
int length2 = part2.length();
int i1 = 0, i2 = 0;
for (int i = 0; i < length; ++i)
if (i1 < length1 && s.charAt(i) == part1.charAt(i1))
++i1;
else if (i2 < length2 && s.charAt(i) == part2.charAt(i2))
++i2;
else
return false;
return i1 == length1 && i2 == length2;
}
public static void main(String[] args) throws IOException {
System.out.println(isMerge("codewars", "cdw", "oears"));
System.out.println(isMerge("codewars", "codewars", ""));
System.out.println(isMerge("codewars", "cdw", "oearsEXTRA"));
}
output:
true
true
false
you have an error on the first part loop you must append from the part not the s char
you don't need to chek the id of the part with the inital word (remove this check (j == I) and this one (j == i)
Use counter to avoid looping many times
Try this code :
public static boolean isMerge(String s, String part1, String part2) {
StringBuilder MergedWord = new StringBuilder("");
String Whole = part1+part2;
int counter1=0;
int counter2=0;
for(int w = 0; w < Whole.length(); w++){
for(int c1 = counter1; c1 < part1.length(); c1++){
if((s.charAt(w) == part1.charAt(c1)) ){
MergedWord.append(part1.charAt(c1)+"");
counter1++;
continue;
}
else
break;
}
for(int c2 = counter2; c2 < part2.length(); c2++){
if((s.charAt(w) == part2.charAt(c2)) ){
MergedWord.append(part2.charAt(c2) + "");
counter2++;
continue;
}
else
break;
}
}
return s.equals(MergedWord.toString())? true : false;
}

Each substring of a certian length of a binary substring should have at least one '1' character

You have been given a binary string containing only the characters '1' and '0'.
Calculate how many characters of the string need to be changed in order to make the binary string such that each of its substrings of at least a certain length contains at least one "1" character.
I came to think of the following idea but it fails for many testcases:
public static int minimumMoves(String s, int d) {
int n = s.length();
int i=0, answer = 0;
while(i<n)
{
boolean hasOne = false;
int j=i;
while(j<n && j<i+d)
{
if(s.charAt(j) == '1')
{
hasOne = true;
break;
}
j++;
}
if(!hasOne) {
answer++;
i += d;
}
else i++;
}
return answer;
}
Also my algorithm runs on O(|s|2) time. Can anyone suggest ideas on O(|s|) time?
Just throwing off an idea:
return s.split("(?<=\\G.{" + String.valueof(d) + "})").stream().filter(str -> str.contains("1")).count()
You just need to break ensure there is no run of d zeros.
public static int minimumMoves(String s, int d) {
int result = 0;
int runLength = 0;
for(char c: s.toCharArray()) {
if (c == '0') {
runLength += 1;
if (runLength == d) { // we need to break this run
result += 1;
runLength = 0;
}
} else {
runLength = 0;
}
}
return result;
}
I used the sliding window technique and Deque to solve this. This is my accepted solution:
public static int minimumMoves(String s, int d) {
int n = s.length();
Deque<Character> dq = new LinkedList<>();
int count = 0, answer = 0;
for(int i=0; i<d; i++)
{
if(s.charAt(i) == '1') count++;
dq.addLast(s.charAt(i));
}
if(count == 0) {
answer++;
count++;
dq.removeLast();
dq.addLast('1');
}
int i=d;
while(i<n)
{
if(dq.getFirst() == '1') count--;
dq.removeFirst();
if(s.charAt(i) == '1') count++;
dq.addLast(s.charAt(i));
if(count == 0)
{
answer++;
dq.removeLast();
dq.addLast('1');
count++;
}
i++;
}
return answer;
}
You just need to use a sliding window and a count of 1s so far at each index. Use a sliding window of d and if you don't see any ones so far, update the last index of that window with 1 and increment the result.
Code below:
public static int minimumMoves(String s, int d) {
int n = s.length();
int[] count = new int[n+1];
int res = 0;
for ( int i = 1; i <= d; i++ ) {
if ( s.charAt(i-1) == '1') count[i] = count[i-1]+1;
else count[i] = count[i-1];
}
if ( count[d] == 0 ) {
res++;
count[d] = 1;
}
for ( int i = d+1; i <= n; i++ ) {
if ( s.charAt(i-1) == '0' ) {
count[i] = count[i-1];
int ones = count[i] - count[i-d];
if ( ones == 0 ) {
count[i] = count[i-1] + 1;
res++;
}
} else {
count[i] = count[i-1] + 1;
}
}
return res;
}
Thought of another implementation you can do for this by working from the maximum possible changes (assumes at start that all values are '0' in String), reduce it when it finds a '1' value, and then jump to the next substring start. This allows it to run in O(n) and Ω(n/m) (n = String length, m = Substring length).
public static int minimumMoves(String s, int d)
{
char[] a = s.toCharArray();
//Total possible changes (not counting tail)
if(a.length < d)
return 0;
int t = (int) a.length / d;
//Total possible changes (counting tail)
//int t = (int) Math.ceil((double) a.length / (double) d);
for(int i = 0; i < a.length; i++)
{
if(a[i] == '1')
{
t--;
//Calculate index for start of next substring
i = (i / d + 1) * d - 1;
}
}
return t;
}

Longest String With Chars Palindrome

Problem description from school assignment
Longest String With Palindrome
I'm getting complexity O(N^2). How can I achieve O(N*log(N))**
My code
int maxL = 0;
for (int i = 0; i < S.length(); i++) {
String currentString = String.valueOf(S.charAt(i));
for (int j = i + 1; j < S.length(); j = j + 1) {
String jStr = String.valueOf(S.charAt(j));
if (currentString.contains(jStr)) {
currentString = currentString.replace(jStr, "");
int len = j - i + 1;
if (currentString.length() == 0 && maxL < len) {
maxL = len;
}
} else {
currentString = currentString + jStr;
}
}
}
return maxL;
This problem can be solved in O(n) time using O(n) space. The following algorithm uses a bit set to keep track of the unbalanced characters for the substrings starting at the beginning of the given string. It makes a single pass through the string and remembers the states it has already seen in a hash map. Whenever we see the same state a second time, we have found a valid password: just remove the old shorter substring from the beginning of the current substring.
private static int index(char c) {
if (c < '0') throw new IllegalArgumentException("illegal char");
if (c <= '9') return c - '0';
if (c < 'a') throw new IllegalArgumentException("illegal char");
if (c <= 'z') return c - 'a' + 10;
throw new IllegalArgumentException("illegal char");
}
private static int solution(String s) {
HashMap<BitSet, Integer> states = new HashMap<>();
int longest = 0;
BitSet state = new BitSet();
states.put((BitSet) state.clone(), 0);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
state.flip(index(c));
Integer seenAt = states.get(state);
if (seenAt != null) {
int len = i - seenAt + 1;
if (len > longest) longest = len;
} else {
states.put((BitSet) state.clone(), i + 1);
}
}
return longest;
}

Own String parser java which solves mathematical equations wrote down in a string

I've wrote a method/function in Java which returns the result of a given basic equation. This equation will be given as a String and I think I got this method working but don't know why I need this one line of Code because this should work without it. After trying for more than an hour to solve it I gave up and hope you can give me an aswer.
Here the Code:
public static double format(String s) {
char[] c = s.toCharArray();
if(s.contains("(")) {
int openbrackets = 0;
for (int i = 0; i < s.length() - 2; i++) {
if (c[i] == '(') openbrackets++;
else if (c[i] == ')') {
openbrackets--;
if(openbrackets == 0) {
s = s.replace(s.substring(s.indexOf('('), i+1), ""+(format(s.substring(s.indexOf('(')+1, i))));
break;
}
}
}
}
if (s.contains("(")) { // String can still contains brackets
s = "" + format(s);
}
c = s.toCharArray();
for(int i = c.length-1; i >= 0; i--) {
if(c[i] == '+') {
return format(s.substring(0, i)) + format(s.substring(i+1, s.length()));
} else if(c[i] == '-') {
return format(s.substring(0, i)) - format(s.substring(i+1, s.length()));
}
}
for(int i = s.length()-1; i > 0; i--) {
if(c[i] == '*') {
return format(s.substring(0, i)) * Double.parseDouble(s.substring(i+1, s.length()));
} else if (c[i] == '/') {
return format(s.substring(0, i)) / Double.parseDouble(s.substring(i+1, s.length()));
}
}
return s.equals("") ? 0 : Double.parseDouble(s); // I don't understand why I need to do this line
}
Description:
I don't know why I need this s.equals("") ? : because the String never should be empty however when I run it with this equation ((23)+(23-23-432-35-1-2-4231+2312+12323-(-3))*3/2) for example I get an error without it.
I need the parser to convert config Strings into Numbers for example when it comes to screenresolution. I know I can also use Libraries but I want to try these things by myself.
PS: Dont hate me just because I don't use libraries. I really tried to figure it out and I have fun doing it. I would just like to know why I have to write this little Codeline as I don't figure it out...
Edit: Error was a NumberFormatException as the Parsing got an empty String... Got my error now also the OverflowException which was mentioned in the comments...
EDIT: To everyone who MIGHT use something like this in the future:
Here the Code which actually works:
public static double format(String s) {
s = s.replace(" ", "");
s = s.replace("\t", "");
char[] c = s.toCharArray();
if(s.contains("(")) {
int openbrackets = 0;
for (int i = 0; i < s.length(); i++) {
if (c[i] == '(') openbrackets++;
else if (c[i] == ')') {
openbrackets--;
if(openbrackets == 0) {
s = s.replace(s.substring(s.indexOf('('), i+1), ""+(format(s.substring(s.indexOf('(')+1, i))));
break;
}
}
}
}
if (s.contains("(")) s = "" + format(s);
c = s.toCharArray();
for(int i = c.length-1; i > 0; i--) {
if(c[i] == '+') {
return format(s.substring(0, i)) + format(s.substring(i+1, s.length()));
} else if(c[i] == '-') {
return format(s.substring(0, i)) - format(s.substring(i+1, s.length()));
}
}
for(int i = s.length()-1; i > 0; i--) {
if(c[i] == '*') {
return format(s.substring(0, i)) * Double.parseDouble(s.substring(i+1, s.length()));
} else if (c[i] == '/') {
return format(s.substring(0, i)) / Double.parseDouble(s.substring(i+1, s.length()));
}
}
return s.equals("") ? 0 : Double.parseDouble(s);
}
I'm fairly sure this is at least one location in your code where you pass a 0 length string to your format function:
c = s.toCharArray();
for(int i = c.length-1; i >= 0; i--) {
if(c[i] == '+') {
return format(s.substring(0, i)) + format(s.substring(i+1, s.length()));
} else if(c[i] == '-') {
return format(s.substring(0, i)) - format(s.substring(i+1, s.length()));
}
}
Your loop counter in (int i = c.length-1; i >= 0; i--) will get decremented until it is 0 in value if there are no + or - values in the input string.
Then you call format(s.substring(0, i)) where i = 0 so I think this is one place where you will be passing a zero length/empty string to your function.
Please use a debugger and step through your code - not only would it teach you a valuable skill it would also probably give you the answer you're looking for.

Zigzag conversion

Question is : The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: "PAHNAPLSIIGYIR"
I have written below code, appearantly it works fine, but I might miss some corner cases. Could you help me to find all the corner cases for this question on my answer?
public static String zigZagConversion(String s , int rowNum){
if (s == null){
throw new IllegalArgumentException();
}
if (rowNum == 1){
return s;
}
StringBuilder str = new StringBuilder();
int step = 2 * rowNum - 2 ;
for (int i = 0 ; i < rowNum ; i++){
if( i == 0 || i == rowNum -1){
for (int j = i ; j < s.length() ; j +=step){
str.append(s.charAt(j));
}
}
else{
int step2 = 2* (rowNum - i - 1);
int step3 = step - step2;
int k = i;
boolean flag = true;
while (k < s.length()){
str.append(s.charAt(k));
if(flag){
k += step2;
flag = false;
}
else{
k +=step3;
flag = false;
}
}
}
}
return str.toString();
}
It gives incorrect output for "PAYPALISHIRING", 4
P I N
A L S I G
Y A H R
P I
So the correct answer should be PINALSIGYAHRPI.
But your program gives PINALIGYAIHRNPI:
an "S" is missing, one extra "I" and one extra "N".
Your revised version is still incorrect, it gives PINALSIIGYAHNPI.
The problem is in the while loop in the middle.
You need to alternate the step counting,
setting the flag on and off.
Your mistake was to only set it off once, and never back on again.
str.append(s.charAt(k));
if (flag) {
k += step2;
flag = false;
} else {
k += step3;
flag = true;
}
With this correction, I believe your solution is correct. (I also added a minor improvement there, extracting the common str.append(s.charAt(k)); from the if-else branches.
My solution on leetcode forum:
https://leetcode.com/problems/zigzag-conversion/discuss/549451/Java-Solution-O(n)-with-algorithm
The mathematic algorithm for zigzag is:
originalDiff = numRows * 2 - 2;
If -> 'currRow' equals First or last lines
use the originalDiff (numRows * 2 - 2)
Else ->
For each new line:
upperDiff += 2,
lowerDiff -=2
Examples:
numRows =2 -> originalDiff = 2
PYAIHRN
APLSIIG
3 -> 4
P A H N
A P L S I I G
Y I R
numRows = 4 -> originalDiff = 6
P I N
A L S I G
Y A H R
P I
numRows = 5 -> originalDiff = 8
P H
A SI
Y I R
P L I G
A N
*/
My solution:
class Solution {
public String convert(String s, int numRows) {
if(numRows == 1) {
return s;
}
String newString = "";
int originalDiff = numRows * 2 - 2;
int diff = originalDiff;
int upperDiff = 0;
boolean isGoingDown = true;
int currIndex = 0;
int currRow = 0;
int startingIndex = 0;
for(int i = 0; i < s.length(); i++) {
System.out.println(currIndex);
newString += s.charAt(currIndex);
if(currRow == 0 || currRow == numRows - 1) {
currIndex += originalDiff;
} else {
if(isGoingDown) {
currIndex += diff;
isGoingDown = !isGoingDown;
} else {
currIndex += upperDiff;
isGoingDown = !isGoingDown;
}
}
if(currIndex >= s.length()) {
currRow++;
diff -= 2;
upperDiff += 2;
currIndex = currRow;
isGoingDown = true;
}
if(currRow == numRows) {
i = s.length();
}
}
return newString;
}
}
Zigzag conversion from leetcode in Javascript
Solution
const zigzag = (str, num) => {
if (num === 1) {
return str;
}
let check = true;
let result = [];
let i = 0;
while (i < str.length) {
result.push([]);
let j = 0;
while (j < num) {
if (check){
result[result.length-1].push(str[i]);
i++;
} else {
if (j == 0) {
result[result.length-1].push(null);
} else if (j === num-1) {
result[result.length-1].unshift(null);
} else {
result[result.length-1].unshift(str[i]);
i++;
}
}
j++;
}
check = !check;
}
let zigzag = [];
for (let k = 0; k < num; k++){
for(let l = 0; l < result.length; l++) {
zigzag.push(result[l][k]);
}
}
return zigzag.join("");
}
Example Input
zigzag("ABCD", 3)
Output
ABDC
Run
https://repl.it/#VinitKhandelwal/zigzag-conversion-javascript
Using HashMap
public String convert(String s, int numRows) {
if (numRows == 1){
return s;
}
StringBuilder result = new StringBuilder();
Map<Integer, StringBuilder> map = new HashMap<>();
for (int i = 0; i < numRows; i++) {
map.put(i,new StringBuilder());
}
int it = 0;
boolean flip = true;
for (int i = 0; i < s.length(); i++) {
if (flip) {
if(it<s.length()){
map.get(it).append(s.charAt(i));
it++;
}
} else {
map.get(it).append(s.charAt(i));
it--;
}
if (it + 1 == numRows || it == 0)
flip = !flip;
}
for (Map.Entry entry: map.entrySet()) {
result.append(entry.getValue());
}
return result.toString();
}
My Solution is traversing the string in the same way it is said in the problem, it is better to make string array of size numrows and the rest is storing the string character as it is in the logic,
you can keep the index and when that index is 0 i.e at the starting then we have to go till the end of the row and then except for first and last row, every array will have diagonal element.
So after traversing till the end then assign index = numrows - 2 and save in the respective array string and decrease and do the same till index >0 and then again traverse till the end row, do this and when we reach the end of the string then break from the loop.
and then concate all the string of string array in a new res string.
class Solution {
public String convert(String s, int n) {
if(n==1 || n>=s.length())
return s;
String[] a = new String[n]; //string array
int ind=0; // index for the string array
boolean flag=true;
int cnt=0; //to keep the counter till where we have traversed the string
while(true && flag)
{
if(ind==0)
{
for(int i=0;i<n;i++)
{
a[i] += s.charAt(cnt);
cnt++;
if(cnt==s.length())
{
flag=false;
break;
}
} // here it has reached the end so we assign here
ind = n-2;
}
else if(ind>0 && ind<n && flag)
{
a[ind] += s.charAt(cnt);
cnt++;
if(cnt==s.length())
{
flag=false;
break;
}
ind--; // to move diagonally up
}
}
String res = new String("");
for(int i=0;i<a.length;i++)
{
// System.out.println(a[i].substring(4));
res += a[i].substring(4);
}
return res;
}
}
Following is the simple solution.
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows <= 1:
return s
res = ""
p = numRows * 2 - 2
temp = p
for i in range(0,numRows):
index = i
flag = 0
while index < len(s):
res = res + s[index]
if i == 0 or i == numRows-1:
index = index + p
else:
if flag == 0:
index = index + temp
flag = 1
else:
index = index + p-temp
flag = 0
temp = temp - 2
return res
zigzag-conversion Complete JavaScript-based solution
Created an Array of an array of row lengths. The main motive is to arrange characters in 2D array form and concat string row-wise.
var convert = function(s, numRows) {
let array =[],c=0,str='';
for(let row =0; row<numRows ; row++) {
array[row] = new Array();
}
while(c < s.length) {
for(let row =0; row<numRows ; row++) {
if((row+1)%numRows ==0) {
array[row].push(s[c]);
c++;
break;
} else {
array[row].push(s[c]);
c++;
}
}
for(let rr = numRows-2 ; rr>0;rr--) {
array[rr].push(s[c]);
c++;
}
}
for(let row =0; row<numRows ; row++) {
for(let i=0;i<array[row].length;i++){
if(array[row][i]){
str+=array[row][i]
}
}
}
return str
};
convert("PAYPALISHIRING",3)

Categories