I have a input like
string input = \"14 + 2 * 32 / 60 + 43 - 7 + 3 - 1 + 0 * 7 + 87 - 32 / 34\";
// up to 10MB string size
int result = Calc(input); // 11
The following solution is a finite automaton. Calc(input) = O(n). For better performance, this solution does not use IndexOf
, Substring
, Parse
, string concatenation, or repeated reading of value (fetching input[i]
more than once)... just a character processor.
static int Calculate1(string input)
{
int acc = 0;
char last = ' ', operation = '+';
for (int i = 0; i < input.Length; i++)
{
var current = input[i];
switch (current)
{
case ' ':
if (last != ' ')
{
switch (operation)
{
case '+': acc += last - '0'; break;
case '-': acc -= last - '0'; break;
case '*': acc *= last - '0'; break;
case '/': acc /= last - '0'; break;
}
last = ' ';
}
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (last == ' ') last = current;
else
{
var num = (last - '0') * 10 + (current - '0');
switch (operation)
{
case '+': acc += num; break;
case '-': acc -= num; break;
case '*': acc *= num; break;
case '/': acc /= num; break;
}
last = ' ';
}
break;
case '+': case '-': case '*': case '/':
operation = current;
break;
}
}
if (last != ' ')
switch (operation)
{
case '+': acc += last - '0'; break;
case '-': acc -= last - '0'; break;
case '*': acc *= last - '0'; break;
case '/': acc /= last - '0'; break;
}
return acc;
}
And another implementation. It reads 25% less from the input. I expect that it has 25% better performance.
static int Calculate2(string input)
{
int acc = 0, i = 0;
char last = ' ', operation = '+';
while (i < input.Length)
{
var current = input[i];
switch (current)
{
case ' ':
if (last != ' ')
{
switch (operation)
{
case '+': acc += last - '0'; break;
case '-': acc -= last - '0'; break;
case '*': acc *= last - '0'; break;
case '/': acc /= last - '0'; break;
}
last = ' ';
i++;
}
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (last == ' ')
{
last = current;
i++;
}
else
{
var num = (last - '0') * 10 + (current - '0');
switch (operation)
{
case '+': acc += num; break;
case '-': acc -= num; break;
case '*': acc *= num; break;
case '/': acc /= num; break;
}
last = ' ';
i += 2;
}
break;
case '+': case '-': case '*': case '/':
operation = current;
i += 2;
break;
}
}
if (last != ' ')
switch (operation)
{
case '+': acc += last - '0'; break;
case '-': acc -= last - '0'; break;
case '*': acc *= last - '0'; break;
case '/': acc /= last - '0'; break;
}
return acc;
}
I implemented one more variant:
static int Calculate3(string input)
{
int acc = 0, i = 0;
var operation = '+';
while (true)
{
var a = input[i++] - '0';
if (i == input.Length)
{
switch (operation)
{
case '+': acc += a; break;
case '-': acc -= a; break;
case '*': acc *= a; break;
case '/': acc /= a; break;
}
break;
}
var b = input[i];
if (b == ' ') i++;
else
{
a = a * 10 + (b - '0');
i += 2;
}
switch (operation)
{
case '+': acc += a; break;
case '-': acc -= a; break;
case '*': acc *= a; break;
case '/': acc /= a; break;
}
if (i >= input.Length) break;
operation = input[i];
i += 2;
}
return acc;
}
Results in abstract points: