一道小学数学题,不过要求编程序解答。 ( 积分: 100 )

  • 主题发起人 主题发起人 LeeChange
  • 开始时间 开始时间
L

LeeChange

Unregistered / Unconfirmed
GUEST, unregistred user!
给定一个由数字,+-*/四种运算符和()组成的表达式,要求去掉了输入表达式中不必要的括号。
输入:(1.8+2)*(3*4) 输出:(1.8+2)*3*4
除了去掉多余的括号,不能改变原字符串的顺序。1+2不能变成2+1
最好有代码实现和算法简单说明,算法越多越好,分数可以再加。
 
粗略想了一下:
1. 为当前式子计算一个权值:顶层算符仅出现加减时,为1;出现乘除时,则为2;没有任何算符,为0(即整条式子被括住,如3*(((4+5))))。顶层算符指当前式子中不在任何括号内的算符。当前式子的初始值是整条式子。
2. 若当前式子不是第一层且权值小于或等于括号权值(可设第一层的括号权值为0),则去掉当前式子两端括号。
3. 顺序查找当前式子中的子式(被括号括住的式子),对每条子式:
a. 计算括号权值:括号两端出现加减时,为1;出现乘除时,为2;没有任何算符,为3。
b. 递归本算法
 
你是为了求计算公式而不是求计算答案吧。
 
最好有代码实现来配合算法简单说明,谢谢。
 
直接写不行吗?接个小分
 
const
SOperate : array[0..3] of string = ('+', '-', '*', '/');
MAXLEVEL = 99;
type
PExp = ^TExp;
TExp = record
Level : integer;
v1 :do
uble;
v2 :do
uble;
operate : integer;
e1 : PExp;
Next : PExp;
end;

function Expand(const Src : string) : PExp;
var
i : integer;
c : integer;
cs : string;
s : string;
s1 : string;
s2 : string;
procedure ExpC(const i : integer);
begin
s1 := trim(s1);
if s1 = '' then
s1 := Trim(cs);
s2 := Trim(copy(src, i + 1, length(src) - i));
try
result.v1 := StrToFloat(s1);
except
result.e1 := Expand(s1);
end;
try
result.v2 := StrToFloat(s2);
except
result.Next := Expand(s2);
end;
end;

begin
if Trim(Src) = '' then
begin
result := nil;
exit;
end;
new(result);
zeromemory(result, sizeof(result^));
c := 0;
cs := '';
s := '';
s1 := '';
s2 := '';
for i := 1 to length(Src)do
begin
//(1.8+2)*(3*4)
case ord(src) of
43 : //+
begin
if c > 0 then
cs := cs + src
else
begin
result.operate := 0;
result.Level := 0;
ExpC(i);
exit;
end;
end;
45 : //-
begin
if c > 0 then
cs := cs + src
else
begin
result.operate := 1;
result.Level := 0;
ExpC(i);
exit;
end;
end;
42 : //*
begin
if c > 0 then
cs := cs + src
else
begin
result.operate := 2;
result.Level := 1;
ExpC(i);
exit;
end;
end;
47 : ///
begin
if c > 0 then
cs := cs + src
else
begin
result.operate := 3;
result.Level := 1;
ExpC(i);
exit;
end;
end;
40 : //(
begin
inc(c);
if c > 1 then
cs := cs + src;
end;
41 : //)
begin
dec(c);
if c > 1 then
cs := cs + src;
end;
else
begin
if c > 0 then
cs := cs + src
else
begin
s1 := s1 + src;
end;
end;
end;
end;
result.operate := -1;
if cs <> '' then
result.Level := MAXLEVEL
else
result.Level := -1;
ExpC(i);
end;

function GetExpLevel(const e : PExp):integer;
var
enext : PExp;
begin
result := MAXLEVEL;
if e = nil then
exit;
if e.operate = -1 then
begin
result := GetExpLevel(e.e1);
exit;
end;
enext := e;
while enext <> nildo
begin
if enext.Level < result then
begin
result := enext.Level;
break;
end;
enext := enext.Next;
end;
end;

function ExpToStr(e : PExp):string;
var
s1 : string;
s2 : string;
begin
if e.e1 = nil then
s1 := FloatToStr(e.v1)
else
s1 := ExpToStr(e.e1);
if e.Next = nil then
s2 := FloatToStr(e.v2)
else
s2 := ExpToStr(e.Next);
if e.operate >= 0 then
begin
if e.Level > GetExpLevel(e.e1) then
s1 := '(' + s1 + ')';
if assigned(e.Next) and (e.Next.Level = MAXLEVEL) and (e.Level > GetExpLevel(e.Next)) then
s2 := '(' + s2 + ')';
result := s1 + SOperate[e.operate] + s2
end
else
result := s1;
end;

procedure FreeExp(e : PExp);
begin
if e.e1 <> nil then
FreeExp(e.e1);
if e.Next <> nil then
FreeExp(e.Next);
Dispose(e);
end;

procedure TForm1.Button6Click(Sender: TObject);
var
s : string;
e : PExp;
begin
inherited;
s := edtFrom.Text;
e := Expand(s);
s := ExpToStr(e);
FreeExp(e);
edtTo.Text := s;
end;
 
一觉醒来,发现自己的函数是错的,现更正如下:
function ChangeExp(const Str: string): string;
var
NoParenthesis: Boolean;
StrList: TStrings;
function DelPths(const AStr: string;
B, E: Integer): string;
var
I: Integer;
Vaild: Boolean;
begin
Result := Copy(AStr, B, E - B +1);
//返回最内层括号表达式
Vaild := False;
//假定括号无用
for I := 2 to Length(Result) - 1do
//如果在括号内找到'+'或'-',则假定括号有用
if Result in ['+', '-'] then
begin
Vaild := True;
Break;
end;
if Vaild then
//如果括号假定有用,则检查括号前后
begin
if B > 1 then
if AStr[B - 1] in ['-', '*', '/'] then
Exit;
if E < Length(AStr) then
if AStr[E + 1] in ['*', '/'] then
Exit;
end;
Result := Copy(AStr, B + 1, E - B -1);
//删除括号
end;

function ExamExp(var AStr: string): Boolean;
var
I, B, E: Integer;
begin
Result := False;
{ 检查最内层括号位置B到E }
B := 0;
E := 0;
for I := 1 to Length(AStr)do
case AStr of
'(': B := I;
')': begin
E := I;
Break;
end;
end;
{ 如果只有一个左或右括号,说明原表达式括号不对称,返回True }
if ((B = 0) and (E <> 0)) or
((B <> 0) and (E = 0)) then
begin
Result := True;
NoParenthesis := True;
Exit;
end;
{ 没找到括号 }
if (B = 0) and (E = 0) then
begin
NoParenthesis := True;
Exit;
end;
{ 检查找到的最内层括号(没用就删除),并加入到StrList }
StrList.Add(DelPths(AStr, B, E));
{ 修改表达式,用'Ax'替换内层括号表达式 }
AStr := Copy(AStr, 1, B - 1) + 'A' + IntToStr(StrList.Count - 1) + Copy(AStr, E + 1, Length(AStr));
end;

var
I: Integer;
begin
Result := Str;
//返回原表达式
//Result := StringReplace(Result, ' ', '', [rfReplaceAll]);
//清除空格
StrList := TStringList.Create;
try
{ 重复直到没有括号 }
repeat
if ExamExp(Result) then
//如果括号不对称返回空(''),也可返回错误提示;
begin
Result := '';
StrList.Clear;
end;
until NoParenthesis;
{ 把'Ax'替换回'(exp)' }
for I := StrList.Count - 1do
wnto 0do
Result := StringReplace(Result, 'A' + IntToStr(I), StrList, []);
finally
StrList.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
S := ChangeExp(Edit1.Text);
if S <> '' then
Edit2.Text := S
else
ShowMessage('输入有误');
end;
 
to ysai:
您试试 1+1+1
to ANiDelphi:
您试试 2/(2/2)
看来小学题目也不好做哎。
 
真的不容易哦,增加了被除数检查,还有其它状况吗
缩短帖子,改在上面了
 
没注意/的特殊性,改了一下

直接修改了第一份代码
 
ANiDelphi的,好象多重括号不对,比如1/((1+1))
 
我的还是有错误,1/((5+6))-7
改好了,直接修改了第一份代码
 
哈哈,我有一个特别好的办法,明天告诉你。
 
这个问题简单啊, 运算符优先级问题嘛
我们曾经实现的一个 ORM 里面包括这个问题的应用(OQL 生成 SQL)
按照 + - * / 的优先级就行了
 
先解析公式, 生成语法树, 然后根据语法树生成公式即可
要注意优先级和结合性问题
 
生成容易,优化难——否则Intel的编译器也不会卖许多钱了,呵呵。dreamfly兄只用一
句“注意”就全都摆平了,厉害![:D]
 
还碰到过厉害的:xx算法书上都有.[:D]
 
再次修改了:(
 
creation-zy 兄如果看一下我们的do
bject ORM 框架就知道了,do
bject 根据 OQL 生成的 SQL 是经过格式化的(formated-well), 在从语法树生成 SQL 时简单判断即可, 就能去掉多余的括号等了.
采用语言处理的思路, 可能是稍微复杂点, 不过确实这是最通用的方法了......
 
突然想到了一个投机取巧的办法, 不知可行否?(语言解析虽通用, 但是麻烦啊)
代码如下:
<.
var fs = [
['(1.8+2)*(3*4)', '(1.8+2)*3*4'],
['3*(((4+5)))', '3*(4+5)'],
['1+1+1', '1+1+1'],
['2/(2/2)', '2/(2/2)'],
['1/((1+1))', '1/(1+1)'],
['1/((5+6))-7', '1/(5+6)-7']
]

var Need = false
foreach f = fsdo
var ret = f[0]
// 多次迭代
loop
Need = false
ret = FormatIt(ret)
if not Need then
{exit loop}
end loop
?? ret ~ /t/t ~ (ret = f[1])
end foreach

function FormatIt(f)
// 去除嵌套括号
Result = ''
var reg = System.Text.Regex('/( ( (?>[^()]+) | (?R) )* /)', 'x')
var mat = reg.Match(f)
while mat.Successdo
Result ~= mat.UnmatchedValue
Result ~= ReplaceIt(mat.Value, '^/( (/(.*/)) /)$', '$1')
mat.NextMatch()
end while
Result ~= mat.UnmatchedValue

// 处理前导运算符
var ps = [
['+/-*', '*/'],
['+', '+/-']
]
foreach p = psdo
Result = ReplaceIt(Result, GenPattern(p[0], p[1]), '${A}${B}')
end foreach

// 处理后继运算符
end function

function GenPattern(p0, p1)
var fact = '[/./d]+'
Result = '(?P<A>[%s]) /( (?P<B>%s ([%s]%s)+)+ /)'
.Format(p0, fact, p1, fact)
end function

function ReplaceIt(f, p, r)
Result =f
while Result.RegexIsMatch(p, 'x')do
Result = Result.RegexReplace(p, r, 'x')
Need = true
end while
end function
.>
 

Similar threads

回复
0
查看
1K
不得闲
S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
911
SUNSTONE的Delphi笔记
S
后退
顶部