I am trying to convert this Python Solution in Java. For some reason, my Java Solution is not working. How can this be done correctly?
https://leetcode.com/problems/decode-string/description/
Given an encoded string, return its decoded string. The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.
You may assume that the input string is always valid; there are no extra white spaces, square brackets are well-formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there will not be input like 3a or 2[4].
The test cases are generated so that the length of the output will never exceed 105.
Example 1:
Input: s = "3[a]2[bc]"
Output: "aaabcbc"
Example 2:
Input: s = "3[a2[c]]"
Output: "accaccacc"
Python Solution:
class Solution:
def decodeString(self, s: str) -> str:
stack = []
for char in s:
if char is not "]":
stack.append(char)
else:
sub_str = ""
while stack[-1] is not "[":
sub_str = stack.pop() + sub_str
stack.pop()
multiplier = ""
while stack and stack[-1].isdigit():
multiplier = stack.pop() + multiplier
stack.append(int(multiplier) * sub_str)
return "".join(stack)
Java Attempt:
class Solution {
public String decodeString(String s) {
Deque<String> list = new ArrayDeque<String>();
String subword = "";
String number = "";
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != ']' ) {
list.add(String.valueOf(s.charAt(i)));
}
else {
subword = "";
while (list.size() > 0 && !list.getLast().equals("[") ) {
subword = list.pop() + subword;
}
if (list.size() > 0) list.pop();
number = "";
while (list.size() > 0 && isNumeric(list.getLast())){
number = list.pop() + number;
}
for (int j = 1; (isNumeric(number) && j <= Integer.parseInt(number)); j++) list.add(subword);
}
}
return String.join("", list);
}
public static boolean isNumeric(String str) {
try {
Double.parseDouble(str);
return true;
} catch(NumberFormatException e){
return false;
}
}
}
The reason why your posted code is not working is because the pop() method in python removes the last element by default.
But in Java, the ArrayDeque class's pop() method removes the first element.
In order to emulate the python code with the ArrayDeque, you'll need to use the removeLast() method of the ArrayDeque instance instead.
public class Solution{
public static String decodeString(String s) {
StringBuilder stack = new StringBuilder();
for(char c : s.toCharArray()) {
if(c != ']') {
stack.append(c);
} else {
StringBuilder sub_str = new StringBuilder();
while(stack.charAt(stack.length() - 1) != '[') {
sub_str.insert(0, stack.charAt(stack.length() - 1));
stack.deleteCharAt(stack.length() - 1);
}
stack.deleteCharAt(stack.length() - 1);
StringBuilder multiplier = new StringBuilder();
while(stack.length() > 0 && Character.isDigit(stack.charAt(stack.length() - 1))) {
multiplier.insert(0, stack.charAt(stack.length() - 1));
stack.deleteCharAt(stack.length() - 1);
}
for(int i = 0; i < Integer.parseInt(multiplier.toString()); i++) {
stack.append(sub_str);
}
}
}
return stack.toString();
}
public static void main(String[] args) {
System.out.println( decodeString("3[a2[c]]"));
//Output: "accaccacc"
System.out.println( decodeString("3[a]2[bc]"));
//Output: "aaabcbc"
}
}
Related
Like in question, exercise if it is possible to create a palindromic string of minimum length 3 characters by removing 1 or 2 characters. For example string "abjchba", we can remove letters "jc" and will get palindromic, in this case program should return removed letters so "jc". I know that we can mke palindromic by removing also "ch" but in exercise is that we should remove characters that appear earlier in string. Program should always attempt to create the longest palindromic substring. I wrote methods to reverse String and method to check that string is palindromic:
private static String reverse(String string) {
return new StringBuilder(string).reverse().toString();
}
private static boolean isPalin(String string) {
return string.equals(reverse(string));
}
I also made method to create Palindromic to return symbols we should remove to make palindromic, but beacuse i'm working on sb 'temp' I got exception . Have anyone idea how to fix it and finish exercise?
private static String createPalindrome(String str) {
StringBuilder result = new StringBuilder();
StringBuilder temp = new StringBuilder(str);
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == str.charAt(str.length() - 1 - i)){
continue;
}else {
result.append(str.charAt(i));
temp.deleteCharAt(i);
if (isPalin(temp.toString())){
return result.toString();
}
}
}
return "not possible";
}
Method 1:
Find longest palindromic subsequence(LPS)
Given string: "abjchba"
Longest Palindrome Subsequence: "abhba". Others like "abjba" and "abcba" also are LPS but you want to remove chars that appear earlier so that "abhba".
If (input string length - length of LPS) > 2, return "not possible".
Remove letters from the input string that are not in the LPS.
Start matching string with LPS. 'j' and 'c' won't match. Add them to result and return.
Method 2:
Find longest common subsequence (LCS) between input string and its reverse.
String: "abjchba"
Reverse: "abhcjba"
LCS: Take "abhba" in our case
If (input string length - length of LCS) > 2, return "not possible".
Step 2 will be the same as that of in Method 1 above.
As you are trying for at most 2 deletions, I am thinking if we can do better with time complexity.
I think the simplest way to fix your code is to use recursion. When you find a char that does not match, remove it and call recursively.
// Helper function to remove a character from a string
public static String removeAt(String s, int i)
{
return s.substring(0, i) + s.substring(i + 1);
}
private static String createPalindromeRecursive(String str) {
// Only need to check half the string
for (int i = 0, j = str.length() - 1; i < j; i++, j--) {
// if (something) continue; else {} <- the else is not needed
// because the continue skips to the end of the loop
// or you can negate the condition and don't use continue
if (str.charAt(i) == str.charAt(j)){
continue;
}
String temp = createPalindrome(removeAt(str, i));
// Success. Return the new string
if (null != temp) return temp;
else return null;
}
return str;
}
private static String createPalindrome(String str) {
String palindrome = createPalindromeRecursive(str);
if (palindrome == null || palindrome.length() < str.length() - 2) {
return "not possible";
}
else {
return palindrome;
}
}
using System;
using System.Text;
class MainClass
{
public static String removeAt(String s, int i)
{
return s.Substring(0, i) + s.Substring(i + 1);
}
private static String createPalindromeRecursive(String str)
{
for (int i = 0, j = str.Length - 1; i < j; i++, j--)
{
if (str[i] == str[j])
{
continue;
}
String temp = PalindromeCreator(removeAt(str, i));
if (null != temp) return temp;
else return null;
}
return str;
}
public static string PalindromeCreator(string str)
{
String palindrome = createPalindromeRecursive(str);
StringBuilder result = new StringBuilder();
StringBuilder temp = new StringBuilder(str);
if (palindrome == null || palindrome.Length < str.Length - 2)
{
return "not possible";
}
else
{
for (int i = 0; i < str.Length; i++)
{
if (str[i] == str[(str.Length - 1 - i)])
{
continue;
}
else
{
result.Append(str[i]);
temp.Remove(i, str.Length);
return result.ToString();
}
}
return palindrome;
}
}
static void Main()
{
// keep this function call here
Console.WriteLine(PalindromeCreator(Console.ReadLine()));
}
}
Came across a programming exercise and was stuck. The problem is:
You need to define a valid password for an email but the only
restrictions are:
The password must contain one uppercase character
The password should not have numeric digit
Now, given a String, find the length of the longest substring which
is a valid password. For e.g Input Str = "a0Ba" , the output should
be 2 as "Ba" is the valid substring.
I used the concept of longest substring without repeating characters which I already did before but was unable to modify it to find the solution to above problem. My code for longest substring without repeating characters is:
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
How about
final String input = "a0Ba";
final int answer = Arrays.stream(input.split("[0-9]+"))
.filter(s -> s.matches("(.+)?[A-Z](.+)?"))
.sorted((s1, s2) -> s2.length() - s1.length())
.findFirst()
.orElse("")
.length();
out.println(answer);
Arrays.stream(input.split("[0-9]+")) splits the original string into an array of strings. The separator is any sequence of numbers (numbers aren't allowed so they serve as separators). Then, a stream is created so I can apply functional operations and transformations.
.filter(s -> s.matches("(.+)?[A-Z](.+)?")) keeps into the stream only strings that have at least one upper-case letter.
.sorted((s1, s2) -> s2.length() - s1.length()) sorts the stream by length (desc).
.findFirst() tries to get the first string of the stream.
.orElse("") returns an empty string if no string was found.
.length(); gets the length of the string.
I suggest that you split your String to have an array of strings without digit:
yourString.split("[0-9]")
Then iterate over this array (says array a) to get the longest string that contains one Upper case character:
a[i].matches("[a-z]*[A-Z]{1}[a-z]*");
You can use a simple array. The algorithm to use would be a dynamic sliding window. Here is an example of a static sliding window: What is a Sliding Window
The algorithm should be as follows:
Keep track of 2 indexes of the array of char. These 2 indexes will be referred to as front and back here, representing the front and back of the array.
Have an int (I'll name it up here) to keep track of the number of upper case char.
Set all to 0.
Use a while loop that terminates if front > N where N is the number of char given.
If the next char is not a number, add 1 to front. Then check if that char is upper case. If so, add 1 to up.
If up is at least 1, update the maximum length if necessary.
If the next char is a number, continue checking the following char if they are also numbers. Set front to the first index where the char is not a number and back to front-1.
Output the maximum length.
You can use my solution which runs in O(n) time and finds the longest part without any digit and with a capital letter:
String testString = "skjssldfkjsakdfjlskdssfkjslakdfiop7adfaijsldifjasdjfil8klsasdfŞdijpfjapodifjpoaidjfpoaidjpfi9a";
int startIndex = 0;
int longestStartIndex = 0;
int endIndex = 0;
int index = 0;
int longestLength = Integer.MIN_VALUE;
boolean foundUpperCase = false;
while(index <= testString.length()) {
if (index == testString.length() || Character.isDigit(testString.charAt(index))) {
if (foundUpperCase && index > startIndex && index - startIndex > longestLength) {
longestLength = index - startIndex;
endIndex = index;
longestStartIndex = startIndex;
}
startIndex = index + 1;
foundUpperCase = false;
} else if (Character.isUpperCase(testString.charAt(index))) {
foundUpperCase = true;
}
index++;
}
System.out.println(testString.substring(longestStartIndex, endIndex));
You don't need regular expressions. Just use a few integers to act as index pointers into the string:
int i = 0;
int longestStart = 0;
int longestEnd = 0;
while (i < s.length()) {
// Skip past all the digits.
while (i < s.length() && Character.isDigit(s.charAt(i))) {
++i;
}
// i now points to the start of a substring
// or one past the end of the string.
int start = i;
// Keep a flag to record if there is an uppercase character.
boolean hasUppercase = false;
// Increment i until you hit another digit or the end of the string.
while (i < s.length() && !Character.isDigit(s.charAt(i))) {
hasUppercase |= Character.isUpperCase(s.charAt(i));
++i;
}
// Check if this is longer than the longest so far.
if (hasUppercase && i - start > longestEnd - longestStart) {
longestEnd = i;
longestStart = start;
}
}
String longest = s.substring(longestStart, longestEnd);
Ideone demo
Whilst more verbose than regular expressions, this has the advantage of not creating any unnecessary objects: the only object created is the longest string, right at the end.
I am using modification of Kadane algorithm to search the required password length. You may use isNumeric() and isCaps() function or include inline if statements. I have shown below with functions.
public boolean isNumeric(char x){
return (x>='0'&&x<='9');
}
public boolean isCaps(char x){
return (x>='A'&&x<='Z');
}
public int maxValidPassLen(String a)
{
int max_so_far = 0, max_ending_here = 0;
boolean cFlag = false;
int max_len = 0;
for (int i = 0; i < a.length(); i++)
{
max_ending_here = max_ending_here + 1;
if (isCaps(a.charAt(i))){
cFlag = true;
}
if (isNumeric(a.charAt(i))){
max_ending_here = 0;
cFlag = false;
}
else if (max_so_far<max_ending_here){
max_so_far = max_ending_here;
}
if(cFlag&&max_len<max_so_far){
max_len = max_so_far;
}
}
return max_len;
}
Hope this helps.
There are plenty of good answers here but thought it might be of interest to add one that uses Java 8 streams:
IntStream.range(0, s.length()).boxed()
.flatMap(b -> IntStream.range(b + 1, s.length())
.mapToObj(e -> s.substring(b, e)))
.filter(t -> t.codePoints().noneMatch(Character::isDigit))
.filter(t -> t.codePoints().filter(Character::isUpperCase).count() == 1)
.mapToInt(String::length).max();
If you wanted the string (rather than just the length), then the last line can be replaced with:
.max(Comparator.comparingInt(String::length));
Which returns an Optional<String>.
I'd use Streams and Optionals:
public static String getBestPassword(String password) throws Exception {
if (password == null) {
throw new Exception("Invalid password");
}
Optional<String> bestPassword = Stream.of(password.split("[0-9]"))
.filter(TypeErasure::containsCapital)
.sorted((o1, o2) -> o1.length() > o2.length() ? 1 : 0)
.findFirst();
if (bestPassword.isPresent()) {
return bestPassword.get();
} else {
throw new Exception("No valid password");
}
}
/**
* Returns true if word contains capital
*/
private static boolean containsCapital(String word) {
return word.chars().anyMatch(Character::isUpperCase);
}
Be sure to write some unit tests
public String pass(String str){
int length = 0;
boolean uppercase = false;
String s= "";
String d= "";
for(int i=0;i<str.length();i++){
if(Character.isUpperCase(str.charAt(i)) == true){
uppercase = true;
s = s+str.charAt(i);
}else if(Character.isDigit(str.charAt(i)) == true ){
if(uppercase == true && s.length()>length){
d = s;
s = "";
length = s.length();
uppercase = false;
}
}else if(i==str.length()-1&&Character.isDigit(str.charAt(i))==false){
s = s + str.charAt(i);
if(uppercase == true && s.length()>length){
d = s;
s = "";
length = s.length();
uppercase = false;
}
}else{
s = s+str.charAt(i);
}
}
return d;}
Here is a simple solution with Scala
def solution(str: String): Int = {
val strNoDigit = str.replaceAll("[0-9]", "-")
strAlphas = strNoDigit.split("-")
Try(strAlphas.filter(_.trim.find(_.isUpper).isDefined).maxBy(_.size))
.toOption
.map(_.length)
.getOrElse(-1)
}
Another solution using tail recursion in Scala
def solution2(str: String): Int = {
val subSt = new ListBuffer[Char]
def checker(str: String): Unit = {
if (str.nonEmpty) {
val s = str.head
if (!s.isDigit) {
subSt += s
} else {
subSt += '-'
}
checker(str.tail)
}
}
checker(str)
if (subSt.nonEmpty) {
val noDigitStr = subSt.mkString.split("-")
Try(noDigitStr.filter(s => s.nonEmpty && s.find(_.isUpper).isDefined).maxBy(_.size))
.toOption
.map(_.length)
.getOrElse(-1)
} else {
-1
}
}
This is a dynamic programming problem. You can solve this yourself using a matrix. It is easy enough. Just give it a try. Take the characters of the password as the rows and columns of the matrix. Add the diagonals if the current character appended to the last character forms a valid password. Start with the smallest valid password as the initial condition.
String[] s = testString.split("[0-9]");
int length = 0;
int index = -1;
for(int i=0; i< s.length; i++){
if(s[i].matches("[a-z]*.*[A-Z].*[a-z]*")){
if(length <= s[i].length()){
length = s[i].length();
index = i;
}
}
}
if(index >= 0){
System.out.println(s[index]);
}
//easiest way to do it:
String str = "a0Ba12hgKil8oPlk";
String[] str1 = str.split("[0-9]+");
List<Integer> in = new ArrayList<Integer>();
for (int i = 0; i < str1.length; i++) {
if (str1[i].matches("(.+)?[A-Z](.+)?")) {
in.add(str1[i].length());
} else {
System.out.println(-1);
}
}
Collections.sort(in);
System.out.println("string : " + in.get(in.size() - 1));
This is my solution with c#. I tested a range of strings and it gave me the correct value. Used Split. No Regex or Substrings. Let me know if it works; open to improvements and corrections.
public static int validPassword(string str)
{
List<int> strLength = new List<int>();
if (!(str.All(Char.IsDigit)))
{
//string str = "a0Bb";
string[] splitStrs = str.Split(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
//check if each string contains a upper case
foreach (string s in splitStrs)
{
//Console.WriteLine(s);
if (s.Any(char.IsUpper) && s.Any(char.IsLower) || s.Any(char.IsUpper))
{
strLength.Add(s.Length);
}
}
if (strLength.Count == 0)
{
return -1;
}
foreach (int i in strLength)
{
//Console.WriteLine(i);
}
return strLength.Max();
}
else
{
return -1;
}
}
I think this solution takes care of all the possible corner cases. It passed all the test cases in an Online Judge. It is a dynamic sliding window O(n) solution.
public class LongestString {
public static void main(String[] args) {
// String testString = "AabcdDefghIjKL0";
String testString = "a0bb";
int startIndex = 0, endIndex = 0;
int previousUpperCaseIndex = -1;
int maxLen = 0;
for (; endIndex < testString.length(); endIndex++) {
if (Character.isUpperCase(testString.charAt(endIndex))) {
if (previousUpperCaseIndex > -1) {
maxLen = Math.max(maxLen, endIndex - startIndex);
startIndex = previousUpperCaseIndex + 1;
}
previousUpperCaseIndex = endIndex;
} else if (Character.isDigit(testString.charAt(endIndex))) {
if (previousUpperCaseIndex > -1) {
maxLen = Math.max(maxLen, endIndex - startIndex);
}
startIndex = endIndex + 1;
previousUpperCaseIndex = -1;
}
}
if (previousUpperCaseIndex > -1)
maxLen = Math.max(maxLen, endIndex - startIndex);
System.out.println(maxLen);
}}
function ValidatePassword(password){
var doesContainNumber = false;
var hasUpperCase = false;
for(var i=0;i<password.length;i++){
if(!isNaN(password[i]))
doesContainNumber = true;
if(password[i] == password[i].toUpperCase())
hasUpperCase = true;
}
if(!doesContainNumber && hasUpperCase)
return true;
else
return false;
}
function GetLongestPassword(inputString){
var longestPassword = "";
for(var i=0;i<inputString.length-1;i++)
{
for (var j=i+1;j<inputString.length;j++)
{
var substring = inputString.substring(i,j+1);
var isValid = ValidatePassword(substring);
if(isValid){
if(substring.length > longestPassword.length)
{
longestPassword = substring;
}
}
}
}
if(longestPassword == "")
{
return "No Valid Password found";
}
else
{
return longestPassword;
}
}
Given a string in Java, how can I obtain a new string where all adjacent sequences of digits are reversed?
My code:
import static java.lang.System.*;
public class P2
{
public static void main(String[] args)
{
if(args.length < 1)
{
err.printf("Usage: java -ea P2 String [...]\n");
exit(1);
}
String[] norm = new String[args.length];
for(int i = 0; i<norm.length;i++)
{
norm[i] = args[i];
}
}
public String invertDigits(String[] norm)
{
}
}
And as an example, this is what it should do:
Inputs: 1234 abc9876cba a123 312asd a12b34c56d
1234 -> 4321
abc9876cba -> abc6789cba
a123 -> a321
312asd -> 213asd
a12b34c56d -> a21b43c65d
Although the question is heavily downvoted, the proposed problem seems clear now. I chose to solve it using a regular expression match in a recursive function.
private static String reverseDigits(String s) {
// the pattern will match a sequence of 1 or more digits
Matcher matcher = Pattern.compile("\\d+").matcher(s);
// fetch the position of the next sequence of digits
if (!matcher.find()) {
return s; // no more digits
}
// keep everything before the number
String pre = s.substring(0, matcher.start());
// take the number and reverse it
String number = matcher.group();
number = new StringBuilder(number).reverse().toString();
// continue with the rest of the string, then concat!
return pre + number + reverseDigits(s.substring(matcher.end()));
}
And here's the iterative approach.
private static String reverseDigits(String s) {
//if (s.isEmpty()) return s;
String res = "";
int base = 0;
Matcher matcher = Pattern.compile("\\d+").matcher(s);
while (!matcher.hitEnd()) {
if (!matcher.find()) {
return res + s.substring(base);
}
String pre = s.substring(base, matcher.start());
base = matcher.end();
String number = matcher.group();
number = new StringBuilder(number).reverse().toString();
res += pre + number;
}
return res;
}
String str = "1234";
//indexes
int i = 0, j = str.length()-1;
// find digits (if any)
while (!Character.isDigit(str.charAt(i)) && i < str.length()) {
i++;
}
while (!Character.isDigit(str.charAt(j)) && j >= 0) {
j--;
}
// while we havent searched all the digits
while (i < j) {
// switch digits
str = str.substring(0, i) + str.charAt(j) + str.substring(i + 1, j) + str.charAt(i) + str.substring(j + 1);
i++;
j--;
// find the next digits
while (!Character.isDigit(str.charAt(i)) && i < str.length()) {
i++;
}
while (!Character.isDigit(str.charAt(j)) && j >= 0) {
j--;
}
}
System.out.println(str);
Another dynamic approach without using regex classes:
public static String reverseOnlyNumbers(String s) {
StringBuilder digits = new StringBuilder();
StringBuilder result = new StringBuilder();
boolean start = false;
for (int i = 0; i < s.length(); i++) {
Character c = s.charAt(i);
if (Character.isDigit(c)) {
start = true;
digits.append(c);
}else {
start = false;
if (digits.length() > 0) {
result.append(digits.reverse().toString());
digits = new StringBuilder();
}
result.append(c);
}
}
return start ? result.append(digits.reverse()).toString() : result.toString();
}
I'm writing a calculator code that solves the input whats given in string. All is good, except when it gets a negative result in the parentheses it fails badly because two operations get next to each other:
1+2*(10-11) >> 1+2*(-1) >> 1+2*-1
So where *- is, it gets "" (nothing) in the BigDecimal's constructor.
I know what's the problem, but how can I solve it?
import java.math.BigDecimal;
import java.util.ArrayList;
public class DoMath {
public static void main(String[] args) {
// Test equation goes here.
String number = "95.3+43.23*(10-11.1)";
System.out.println(doMath(number));
}
public static BigDecimal doMath(String input) {
StringBuilder builtInput = new StringBuilder(input);
StringBuilder help = new StringBuilder();
// Check if there are parenthesis in the equation.
boolean noParenthesis = true;
for (int i = 0; i < builtInput.length(); i++) {
if (builtInput.charAt(i) == 40) {
noParenthesis = false;
break;
}
}
if (noParenthesis) { // If there are no parenthesis, calculate the equation!
return calculateAndConvert(builtInput);
} else { // If there are parenthesis, breakdown to simple equations!
int parenthesePair = 0;
// Start extracting characters from the builtInput variable.
for (int i = 0; i < builtInput.length(); i++) {
// Start where we find a parentheses opener.
if (builtInput.charAt(i) == 40) {
parenthesePair = 1;
builtInput.deleteCharAt(i);
for (int j = i; j < builtInput.length(); j++) {
// If we find another opener, add one to parenthesePair variable.
if (builtInput.charAt(j) == 40) {
parenthesePair++;
}
// If we find a closer, subtract one from the given variable.
if (builtInput.charAt(j) == 41) {
parenthesePair--;
}
// If we have found the matching pair, delete it and break the for loop.
if (parenthesePair == 0) {
builtInput.deleteCharAt(j);
builtInput.insert(j, doMath(help.toString()));
break;
}
help.append(builtInput.charAt(j));
builtInput.deleteCharAt(j);
j--;
}
break;
}
}
}
System.out.println(builtInput);
return doMath(builtInput.toString());
}
public static BigDecimal calculateAndConvert(StringBuilder input) {
ArrayList<BigDecimal> listOfNumbers = new ArrayList<BigDecimal>();
StringBuilder numBay = new StringBuilder();
StringBuilder operations = new StringBuilder();
// If the first character is -, the first number is negative.
boolean firstIsNegative = false;
if (input.charAt(0) == 45) {
firstIsNegative = true;
input.deleteCharAt(0);
}
// Converting to numbers.
while (input.length() != 0) {
// If the character is a number or a dot, put it in the numBay variable and delete the char.
if (input.charAt(0) >= 48 && input.charAt(0) <= 57 || input.charAt(0) == 46) {
numBay.append(input.charAt(0));
// If the character is not a number, put it in the operations variable
// and save the number in the list (not operator characters are filtered)
} else {
listOfNumbers.add(new BigDecimal(numBay.toString()));
numBay.setLength(0);
operations.append(input.charAt(0));
}
// Delete the character.
input.deleteCharAt(0);
}
listOfNumbers.add(new BigDecimal(numBay.toString()));
// Setting first number to negative if it's needed.
if (firstIsNegative) {
listOfNumbers.set(0, listOfNumbers.get(0).negate());
}
// Calculate the result from the list and operations and return it.
return calculate(listOfNumbers, operations);
}
public static BigDecimal calculate(ArrayList<BigDecimal> list, StringBuilder ops) {
BigDecimal momentaryResult;
// Check for a multiply operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 42) {
momentaryResult = list.get(i).multiply(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Check for a divide operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 47) {
momentaryResult = list.get(i).divide(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Check for a subtract operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 45) {
momentaryResult = list.get(i).subtract(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Check for a plus operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 43) {
momentaryResult = list.get(i).add(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Return with the one remaining number that represents the result.
return list.get(0);
}
}
Edit: or would it be easier to write a new code with a different algorithm...?
I would post this as a comment to your question, but I do not have the required reputation to do so.
Anyway, since you have already recognized that the bug is the "operator" *- couldn't you make a method that would fix this problem by replacing the plus operator immediately before by a minus? Like this:
1+2*-1 >>> 1-2*1
If you want I can write you the code. But maybe it will be easier for you to adapt a solution like this in your code that is already working.
Edit - 1:
Obviously, the code should also treat the following cases:
1-2*-1 >>> 1+2*1
2*-1 >>> -2*1
Edit - 2:
Here is the code I managed to make. Let me know if you find any errors.
public int countChar(String str, char chr) {
int count = 0;
for (int k = 0; k < str.length(); k++) {
if (str.charAt(k) == chr)
count++;
}
return count;
}
public String fixBug(String eq) {
boolean hasBug = eq.contains("*-");
if (hasBug) {
String subeq;
int indbug, indp, indm;
eq = eq.replace("*-", "#");
int N = countChar(eq, '#');
for (int k = N; k > 0; k--) {
indbug = eq.indexOf('#');
subeq = eq.substring(0, indbug);
indp = subeq.lastIndexOf('+');
indm = subeq.lastIndexOf('-');
if (indp == -1 && indm == -1) {
eq = "-" + eq;
} else if (indp > indm) {
eq = eq.substring(0, indp) + '-' + eq.substring(indp + 1);
} else {
eq = eq.substring(0, indm) + '+' + eq.substring(indm + 1);
}
}
eq = eq.replace("#", "*");
}
return eq;
}
I'm dealing with logical expressions in strings. So far I have worked on the following method.
public static String modify(String expression)
{
String temp = expression;
String validated = "";
for(int idx=0; idx<temp.length(); idx++)
{
if(idx!=temp.length()-1)
{
if((Character.isAlphabetic(temp.charAt(idx))) && (Character.isAlphabetic(temp.charAt(idx+1))))
{
validated+=temp.substring(idx,idx+1);
validated+="*";
}
else
validated+=temp.substring(idx,idx+1);
}
else
validated+=temp.substring(idx);
}
return validated;
}
The following are examples of supposed input/output
input: AB+BC+ABC / output: (A*B)+(B*C)+(A*B*C)
input: (A+B)+ABC / output: (A+B)+(A*B*C)
input: (A+B)*(B+C)*(AB) / output: (A+B)*(B+C)*(A*B)
One way you can do it is simply keeping track of brackets with a boolean semaphore
public static String modify(String expression)
{
String temp = expression;
StringBuilder validated = new StringBuilder();
boolean inBrackets=false;
for(int idx=0; idx<temp.length()-1; idx++)
{
if((Character.isLetter(temp.charAt(idx))) && (Character.isLetter(temp.charAt(idx+1))))
{
if(!inBrackets){
inBrackets = true;
validated.append("(");
}
validated.append(temp.substring(idx,idx+1));
validated.append("*");
}
else{
validated.append(temp.substring(idx,idx+1));
if(inBrackets){
validated.append(")");
inBrackets=false;
}
}
}
validated.append(temp.substring(temp.length()-1));
if(inBrackets){
validated.append(")");
inBrackets=false;
}
return validated.toString();
}
Also never use string concatenation instead use StringBuilder or its predecessor StringBuffer in case you are seeking thread safe solution.
Here is what I would do, using StringBuilder and a split:
public static String modify(String expression)
{
StringBuilder finalString = new StringBuilder();
String[] subExpressions = expression.split("\\+");
List<String> formattedSubExpressions = new ArrayList<String>();
for (String subExpression : subExpressions) {
if (subExpression.length() > 1) {
StringBuilder formattedSubExpression = new StringBuilder();
formattedSubExpression.append("(");
for (int i=0; i<subExpression.length(); i++) {
formattedSubExpression.append(subExpression.charAt(i));
if (i != subExpression.length() -1 ) {
formattedSubExpression.append("*");
}
}
formattedSubExpression.append(")");
formattedSubExpressions.add(formattedSubExpression.toString());
} else {
formattedSubExpressions.add(subExpression);
}
}
for (String subExpression : formattedSubExpressions) {
finalString.append(subExpression);
finalString.append("+");
}
if (finalString.charAt(finalString.length() - 1) == '+') {
finalString.deleteCharAt(finalString.length() - 1);
}
return finalString.toString();
}
It gives the following sample input/output:
AB+CD: (A*B)+(C*D)
AB+CD+EF: (A*B)+(C*D)+(E*F)
AB+CD+EFGH: (A*B)+(C*D)+(E*F*G*H)
I based this answer on the idea that what you want to do is group repeating alpha characters between parentheses and put an asterisks between them regardless of the operation (add, subtract, divide, etc) being performed between the groups.
private static final Pattern p = Pattern.compile("[a-zA-Z]{2,}");
public String parse(String s){
if(s == null || "".equals(s)) {
return s;
}
char[] chars = s.toCharArray();
StringBuilder sb = new StringBuilder(100);
Matcher m = p.matcher(s);
int i = 0;
while(i<chars.length && m.find()){
int startIdx = m.start();
int endIdx = m.end();
// Need to get the leading part of the string before this matching region
while(i < startIdx){
sb.append(chars[i]);
i++;
}
sb.append('('); // Start getting the match region
while(i < endIdx){
sb.append(chars[i]);
if(i < endIdx - 1){
sb.append('*');
}
i++;
}
sb.append(')'); // end the match region
}
// If there is a region beyond the last match, append it
if(i < chars.length -1){
for(; i < chars.length; i++){
sb.append(chars[i]);
}
}
return sb.toString();
}