程序员面试金典: 9.9 递归和动态规划 9.11求布尔表达式的表达个数

#include 
#include 
#include 
#include 
#include 

using namespace std;

/*
问题:给定一个布尔表达式,由0、1、&、|和^等符号组成,以及一个想要的布尔结果result,实现一个函数,算出有几种括号的方法可使该
      表达式得出result值。
分析:书上解法,采用以符号作为分割,符号左边和符号右边分别加表达式的方式进行计算。
     比如表达式为: 1^0|0|1,
	               以第一个符号"^"拆分为 1^(0|0|1)
				       二                (1^0)|(0|1)
					   ....
	 最关键的就是进行表达式等价转换:假设表达式为exp,拆分为左右表达式我exp1,exp2,
	 设f(exp1|exp2 , result)为表达式f(exp,result)是表达式exp符合result的结果集个数
	 当result=true时,
	 与运算为true,则exp1和exp2必须都为true
	 f(exp1 & exp2 , true) = f(exp1 ,true) * f(exp2 , true) 【1】	 
	 或运算为true,分三种情况:两个都为true,exp1为true且exp2为false,exp1为false且exp2位true
	 f(exp1 | exp2 , true) = f(exp1 , true) * f(exp2,true)+f(exp1, true)*f(exp2,false)+f(exp1,false)*f(exp2,true)【2】
	 异或运算为true,必定一个为true,一个为false
	 f(exp1 ^ exp2 , true) = f(exp1,true)*f(exp2,false) + f(exp1,false)*f(exp2,true)【3】

	 当result为false时,
	 与运算为false,两个都为false,或者其中一个为false,一个为true
	 f(exp1 & exp2 , false)=f(exp1 , false) * f(exp2,false)+f(exp1, false)*f(exp2,true)+f(exp1,true)*f(exp2,false) 【4】
	 或运算为false,两个都为flase
	 f(exp1 | exp2 , false) = f(exp1,false)*f(exp2,false) 【5】
	 异或运算为flase,两个同为true或同为flase
	 f(exp1 ^ exp2, false) = f(exp1,true)*f(exp2,true) + f(exp1,false)*f(exp2,false)【6】 

	 采用上述方式递归将表达式拆分,根据result的值,分别采用上述公式【1】~公式【6】进行处理即可
	 发现在程序员面试金典中,所谓的动态规划变成了=记忆化搜索+递归,这其实不是动态规划,而是递归
	 这里递归的本质就是通过符号来将表达式一分为2,对每部分表达式再递归计算

输入:输入第一个字符串为表达式,第二个字符串为表达式的值
1^0|0|1 true
输出:输出通过给表达式括号,可以使得表达式的值为给定布尔值的方法个数
3

关键:
1      比如表达式为: 1^0|0|1,
	               以第一个符号"^"拆分为 1^(0|0|1)
				       二                (1^0)|(0|1)
					   ....
	 最关键的就是进行表达式等价转换:假设表达式为exp,拆分为左右表达式我exp1,exp2,
	 设f(exp1|exp2 , result)为表达式f(exp,result)是表达式exp符合result的结果集个数
	 当result=true时,
	 与运算为true,则exp1和exp2必须都为true
	 f(exp1 & exp2 , true) = f(exp1 ,true) * f(exp2 , true) 【1】	 
	 或运算为true,分三种情况:两个都为true,exp1为true且exp2为false,exp1为false且exp2位true
	 f(exp1 | exp2 , true) = f(exp1 , true) * f(exp2,true)+f(exp1, true)*f(exp2,false)+f(exp1,false)*f(exp2,true)【2】
	 异或运算为true,必定一个为true,一个为false
	 f(exp1 ^ exp2 , true) = f(exp1,true)*f(exp2,false) + f(exp1,false)*f(exp2,true)【3】

	 当result为false时,
	 与运算为false,两个都为false,或者其中一个为false,一个为true
	 f(exp1 & exp2 , false)=f(exp1 , false) * f(exp2,false)+f(exp1, false)*f(exp2,true)+f(exp1,true)*f(exp2,false) 【4】
	 或运算为false,两个都为flase
	 f(exp1 | exp2 , false) = f(exp1,false)*f(exp2,false) 【5】
	 异或运算为flase,两个同为true或同为flase
	 f(exp1 ^ exp2, false) = f(exp1,true)*f(exp2,true) + f(exp1,false)*f(exp2,false)【6】 

	 采用上述方式递归将表达式拆分,根据result的值,分别采用上述公式【1】~公式【6】进行处理即可
	 发现在程序员面试金典中,所谓的动态规划变成了=记忆化搜索+递归,这其实不是动态规划,而是递归
	 这里递归的本质就是通过符号来将表达式一分为2,对每部分表达式再递归计算

2 
	//如果当前只有一个字符,容易遗漏
	if(s == e)
	{
		if( '1' == exp.at(s) && result)
		{
			return 1;
		}
		else if('0' == exp.at(s) && !result)
		{
			return 1;
		}
		return 0;
	}
*/

//计算表达式exp中从下标s到e的表达式中通过加括号符合result的摆放个数
int f(string& exp , bool result , int s , int e , map& expToTimes)
{
	//鲁棒性
	if(exp.empty() || s < 0 || e < 0  )
	{
		return 0;
	}
	//如果之前已经计算过了:字符串起始位置处下为result的次数,就直接返回
	stringstream ss;
	ss << result << " " << s << " " << e;
	string key = ss.str();
	if(expToTimes.find(key) != expToTimes.end())
	{
		return expToTimes.find(key)->second;
	}
	//如果当前只有一个字符,容易遗漏
	if(s == e)
	{
		if( '1' == exp.at(s) && result)
		{
			return 1;
		}
		else if('0' == exp.at(s) && !result)
		{
			return 1;
		}
		return 0;
	}
	//下面对表达式进行拆分,s对应了字符,s+1对应符号,所以每次间隔2个字符做递归处理
	int count = 0;
	for(int i = s + 1 ; i <= e ; i += 2)
	{
		if(result)
		{
			switch( exp.at(i) )
			{
			case '&':
				count += f(exp , true , s , i-1 ,expToTimes) * f(exp , true , i+1 , e,expToTimes);
				break;
			case '|':
				count += f(exp , true , s , i-1,expToTimes) * f(exp,true , i+1 , e,expToTimes) + 
					f(exp, true , s , i-1,expToTimes)*f(exp,false, i+1 , e,expToTimes) + 
					f(exp,false , s , i -1 ,expToTimes) * f(exp,true , i+1 , e,expToTimes);
				break;
			case '^':
				count += f(exp,true , s , i-1,expToTimes)*f(exp,false,i+1 , e ,expToTimes) + 
					f(exp,false, s , i-1,expToTimes)*f(exp,true,i+1 , e,expToTimes);
				break;
			default:
				break;
			}
		}
		else
		{
			switch( exp.at(i))
			{
			case '&':
				count += f(exp , false , s , i-1,expToTimes) * f(exp,false,i+1,e,expToTimes) + 
					f(exp, false, s , i-1,expToTimes)*f(exp,true,i+1,e,expToTimes) + 
					f(exp,true, s , i-1,expToTimes)*f(exp,false,i+1,e,expToTimes);
				break;
			case '|':
				count += f(exp,false, s , i-1,expToTimes)*f(exp,false,i+1,e,expToTimes);
				break;
			case '^':
				count += f(exp,true, s , i-1,expToTimes)*f(exp,true,i+1,e,expToTimes) + 
					f(exp,false, s , i-1,expToTimes)*f(exp,false,i+1,e,expToTimes);
				break;
			default:
				break;
			}
		}
	}
	expToTimes.insert(pair(key , count));
	return count;
}

void process()
{
	string exp , result;
	bool bResult;
	map expToTimes;
	while(cin >> exp >> result)
	{
		expToTimes.clear();
		if("true" == result)
		{
			bResult = true;
		}
		else if("false" == result)
		{
			bResult = false;
		}
		else
		{
			cout << "Input is wrong , please input again!" << endl;
			continue;
		}
		//接下来计算
		int result = f(exp , bResult , 0 , exp.size() - 1  , expToTimes);
		cout << result << endl;
	}
}

int main(int argc, char* argv[])
{
	process();
	getchar();
	return 0;
}

你可能感兴趣的:(程序员面试金典)