对于一个只由0(假)、1(真)、&(逻辑与)、|(逻辑或)和^(异或)五种字符组成的逻辑表达式,再给定一个结果值。现在可以对这个没有括号的表达式任意加合法的括号,返回得到能有多少种加括号的方式,可以达到这个结果。
给定一个字符串表达式exp及它的长度len,同时给定结果值ret,请返回方案数。保证表达式长度小于等于300。为了防止溢出,请返回答案Mod 10007的值。
测试样例:
"1^0|0|1",7,0
返回:2
对于一个只由0(假)、1(真)、&(逻辑与)、|(逻辑或)和^(异或)五种字符组成的逻辑表达式,再给定一个结果值。现在可以对这个没有括号的表达式任意加合法的括号,返回得到能有多少种加括号的方式,可以达到这个结果。
给定一个字符串表达式exp及它的长度len,同时给定结果值ret,请返回方案数。保证表达式长度小于等于300。为了防止溢出,请返回答案Mod 10007的值。
"1^0|0|1",7,0
返回:2
class Expression { string str; int DP[333][333][2]; int n; int MOD; void dp() { for(int l=0;l<n;l+=2) { for(int i=0;i+l<n;i+=2) { int v[2] = {0,0}; if(l==0) { int c = str[i] - '0'; DP[i][i+l][c] = 1; DP[i][i+l][c^1] = 0; continue; } for(int j=i;j<i+l;j+=2) { int t[3] = { (DP[i][j][0]%MOD)*(DP[j+2][i+l][0]%MOD), (DP[i][j][0]%MOD)*(DP[j+2][i+l][1]%MOD)+(DP[i][j][1]%MOD)*(DP[j+2][i+l][0]%MOD), (DP[i][j][1]%MOD)*(DP[j+2][i+l][1]%MOD) }; t[0] %= MOD; t[1] %= MOD; t[2] %= MOD; switch (str[j+1]) { case '|': v[0] += t[0]; v[1] += t[1] + t[2]; break; case '&': v[0] += t[0] + t[1]; v[1] += t[2]; break; case '^': v[0] += t[0] + t[2]; v[1] += t[1]; break; } v[0] %= MOD; v[1] %= MOD; } DP[i][i+l][0] = v[0]; DP[i][i+l][1] = v[1]; } } }
public:
int countWays(string exp, int len, int ret) {
str = exp;
n = len;
MOD = 1e4+7; dp(); return DP[0][n-1][ret];
}
};
//参考的JustYoung大神 import java.util.Scanner; public class test7 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int len = scanner.nextInt(); String exp = scanner.next(); int ret = scanner.nextInt(); System.out.println(countWays(exp, len, ret)); } } // 三维dp矩阵 dp[i][j][z],表示从i到j计算结果为z的所有方法总和,这里z的取值只有0和1两种。 // 构造dp[i][j][z]的方法是对dp[i][k][z]*dp[k+1][j][z]求和,k取值范围从i到j-1 public static int countNum(int[] nums, char[] ops, int ret) { int n = nums.length; int[][][] d = new int[n][n][2]; for (int i = 0; i < n; i++) { if (nums[i] == 1) { d[i][i][1] = 1; } else { d[i][i][0] = 1; } } for (int x = 1; x < n; x++) { // 外循环是各个数字位 for (int i = 0; i < n - x; i++) { int j = i + x; for (int k = i; k < j; k++) { if (d[i][k][0] != 0 && d[k + 1][j][0] != 0) { int num = cal(0, 0, ops[k]); d[i][j][num] = (d[i][j][num] + d[i][k][0] * d[k + 1][j][0]) % 10007; } if (d[i][k][0] != 0 && d[k + 1][j][1] != 0) { int num = cal(0, 1, ops[k]); d[i][j][num] = (d[i][j][num] + d[i][k][0] * d[k + 1][j][1]) % 10007; } if (d[i][k][1] != 0 && d[k + 1][j][0] != 0) { int num = cal(1, 0, ops[k]); d[i][j][num] = (d[i][j][num] + d[i][k][1] * d[k + 1][j][0]) % 10007; } if (d[i][k][1] != 0 && d[k + 1][j][1] != 0) { int num = cal(1, 1, ops[k]); d[i][j][num] = (d[i][j][num] + d[i][k][1] * d[k + 1][j][1]) % 10007; } } } } return d[0][n - 1][ret]; } public static int countWays(String exp, int len, int ret) { char[] cs = exp.toCharArray(); int[] nums = new int[(len + 1) / 2]; // 存放数字位 char[] ops = new char[(len - 1) / 2]; // 存放操作符位 for (int i = 0; i < len; i++) { if (i % 2 == 0) { nums[i / 2] = Integer.valueOf(cs[i] - '0'); } else { ops[i / 2] = cs[i]; } } return countNum(nums, ops, ret) % 10007; } public static int cal(int a, int b, char op) { if (op == '^') { return a ^ b; } else if (op == '|') { return a | b; } else { return a & b; } } }
/**
可以运用区间DP的思想
对于一个区间[L,R]来说 我们把它以K为中点分为两份
设该表达式为最后算的一个表达式
也就是现在要算的表达式为[L,k][k+1,k+1][k+2,R]有多少种方案为ret。
先讨论符号 例如exp[k-1]='^'
那么我们有真值表
[L,k] [k+2,R] [L,R]
0 0 0
0 1 1
1 0 1
1 1 0
那么[L,R]中 0的方案数=[L,k]中0的方案数*[k+2,R]中0的方案数+[L,k]中1的方案数*[k+2,R]中1的方案数
这样我们就能求出[L,R]的以K分组的方案数了
注意 我这里说的是以K分组
那求[L,R]的方案数就是分别以L,L+2...R-2分组的方案数之和
这就是区间DP的思想
其他运算符以此类推
*/
class Expression {
string str;
int DP[333][333][2];
int n;
int MOD;
///这里DP采用打表的方法
///其实也可用记忆化搜索 可以理解理解区间DP
void dp()
{
///枚举长度
for(int len=0;len<n;len+=2)
///枚举起始点
for(int i=0;i+len<n;i+=2)
{
///v是当前区间ret=0 的和 与 ret=1 的和
int v[2]={0,0};
///len==0时说明现在区间为[i,i] 应初始化为str的值
if(len==0)
{
int c=str[i]-'0';
DP[i][i+len][c]=1;
DP[i][i+len][c^1]=0;
continue;
}
///j为枚举k点
for(int j=i;j<i+len;j+=2)
{
///t为真值表中左右两个区间对应的方案数
///t[0]为 0 0
///t[1]为 0 1 * 0 1
///t[2]为 1 1
int t[3]=
{
(DP[i][j][0]%MOD)*(DP[j+2][i+len][0]%MOD),
(DP[i][j][0]%MOD)*(DP[j+2][i+len][1]%MOD)+(DP[i][j][1]%MOD)*(DP[j+2][i+len][0]%MOD),
(DP[i][j][1]%MOD)*(DP[j+2][i+len][1]%MOD)
};
t[0]%=MOD;
t[1]%=MOD;
t[2]%=MOD;
///处理运算符
switch (str[j+1])
{
case '|':
v[0]+=t[0];
v[1]+=t[1]+t[2];
break;
case '&':
v[0]+=t[0]+t[1];
v[1]+=t[2];
break;
case '^':
v[0]+=t[0]+t[2];
v[1]+=t[1];
break;
}
v[0]%=MOD;
v[1]%=MOD;
}
///为区间赋值
DP[i][i+len][0]=v[0];
DP[i][i+len][1]=v[1];
}
}
public:
int countWays(string exp, int len, int ret)
{
str=exp;
n=len;
MOD=1e4+7;
dp();
return DP[0][n-1][ret];
// write code here
}
};
import java.util.*;
public class Expression {
public int cal(int d1, int d2, char op) {
if (op == '&') {
return d1 & d2;
} else if (op == '|') {
return d1 | d2;
} else {
return d1 ^ d2;
}
}
public int count(int[] digits, char[] ops, int des) {
int n = digits.length;
int[][][] dp = new int[n][n][2];
for (int i = 0; i < n; ++i) {
if (digits[i] == 1) {
++dp[i][i][1];
} else {
++dp[i][i][0];
}
}
for (int x = 1; x < digits.length; ++x) {
for (int i = 0; i < digits.length - x; ++i) {
int j = i + 1 + x - 1;
for (int k = i; k < j; ++k) {
if (dp[i][k][0] != 0 && dp[k + 1][j][0] != 0) {
int num = cal(0, 0, ops[k]);
dp[i][j][num] = (dp[i][j][num] + dp[i][k][0] * dp[k + 1][j][0]) % 10007;
}
if (dp[i][k][0] != 0 && dp[k + 1][j][1] != 0) {
int num = cal(0, 1, ops[k]);
dp[i][j][num] = (dp[i][j][num] + dp[i][k][0] * dp[k + 1][j][1]) % 10007;
}
if (dp[i][k][1] != 0 && dp[k + 1][j][0] != 0) {
int num = cal(1, 0, ops[k]);
dp[i][j][num] = (dp[i][j][num] + dp[i][k][1] * dp[k + 1][j][0]) % 10007;
}
if (dp[i][k][1] != 0 && dp[k + 1][j][1] != 0) {
int num = cal(1, 1, ops[k]);
dp[i][j][num] = (dp[i][j][num] + dp[i][k][1] * dp[k + 1][j][1]) % 10007;
}
}
}
}
return dp[0][n - 1][des];
}
public int countWays(String exp, int len, int ret) {
// write code here
char[] exps = exp.toCharArray();
int[] digits = new int[(exps.length + 1) / 2];
char[] ops = new char[(exps.length - 1) / 2];
for (int i = 0; i < exps.length; ++i) {
if (i % 2 == 0) {
digits[i / 2] = Integer.valueOf(exps[i] - '0');
} else {
ops[i / 2] = exps[i];
}
}
return count(digits, ops, ret) % 10007;
}
}
import java.util.*;
// DP[i][j][0/1]:区间DP, 结果就01两种
public class Expression {
public int countWays(String exp, int len, int ret) {
char[] s = exp.toCharArray();
int n = s.length, mod = 10007;
int[][][] dp = new int[n][n][2];
for (int i = n - 1; i >= 0; i -= 2) {
dp[i][i][s[i] - '0'] = 1;
for (int j = i + 2; j < n; j += 2) {
for (int k = i; k + 2 <= j; k += 2) { // 枚举分割点
int op = s[k + 1], cnt0 = 0, cnt1 = 0;
int a0 = dp[i][k][0], a1 = dp[i][k][1];
int b0 = dp[k + 2][j][0], b1 = dp[k + 2][j][1];
if (op == '&') {
cnt0 += a0 * (b0 + b1) + a1 * b0;
cnt1 += a1 * b1;
} else if (op == '|') {
cnt0 += a0 * b0;
cnt1 += a0 * b1 + a1 * (b0 + b1);
} else if (op == '^') {
cnt0 += a0 * b0 + a1 * b1;
cnt1 += a0 * b1 + a1 * b0;
}
dp[i][j][0] = (dp[i][j][0] + cnt0) % mod;
dp[i][j][1] = (dp[i][j][1] + cnt1) % mod;
}
}
}
return dp[0][n - 1][ret];
}
} class Expression {
public:
bool isValid(string exp,int len)
{
if(len%2==0) return false;
for(int i=0;i<len;++i)
{
if(i%2==0)
{
if(exp[i]!='0'&&exp[i]!='1') return false;
}
else{
if(exp[i]!='^'&&exp[i]!='|'&&exp[i]!='&')return false;
}
}
return true;
}
int countWays(string exp, int len, int ret) {
// write code here
int mod = 1e4+7;
if(!isValid(exp,len)) return 0;
vector<vector<long long >>t(len,vector<long long>(len));
vector<vector<long long>>f(len,vector<long long>(len));
t[0][0] = exp[0]== '0' ? 0 : 1;
f[0][0] = exp[0]== '0' ? 1 : 0;
for(int i=2;i<len;i+=2)
{
t[i][i] = exp[i] == '0' ? 0 : 1;
f[i][i] = exp[i] == '0' ? 1 : 0;
for(int j=i-2;j>=0;j-=2)
for(int k=j;k<i;k+=2)
{
if(exp[k+1]=='&')
{
t[j][i] += (t[j][k]*t[k+2][i])%mod;
f[j][i] += (t[j][k]*f[k+2][i]+f[j][k]*f[k+2][i]+f[j][k]*t[k+2][i])%mod;
}
else if(exp[k+1]=='|')
{
t[j][i] += (t[j][k]*(f[k+2][i]+t[k+2][i])+ f[j][k]*t[k+2][i])%mod;
f[j][i] += (f[j][k]*f[k+2][i])%mod;
}
else{
t[j][i] += (t[j][k]*f[k+2][i]+f[j][k]*t[k+2][i])%mod;
f[j][i] += (t[j][k]*t[k+2][i]+f[j][k]*f[k+2][i])%mod;
}
}
}
return ret ? (t[0][len-1]%mod):(f[0][len-1]%mod);
}
}; class Expression {
public:
int bit(int left,char op,int right){
if(op=='|') return left|right;
if(op=='^') return left^right;
return left&right;
}
int countWays(string exp, int len, int ret) {
int n=len/2+1;
//dp[i][j][k]表示从i到j结果为k的方案数
vector<vector<vector<int>>> dp(n,vector<vector<int>>(n,vector<int>(2,0)));
string num="",op="";//需要先把数字和运算符分开
for(int i=0;i<len;++i)
if(i%2) op+=exp[i];
else num+=exp[i];
//初值
for(int i=0;i<n;++i)
dp[i][i][num[i]-'0']=1;
for(int length=1;length<n;++length)//长度
for(int left=0;left<n-length;++left){//左端点
int right=left+length;//右端点
for(int mid=left;mid<right;++mid)//加括号的方法可以为(left-mid) op (mid+1,right),方案数为二者相乘
for(int i=0;i<=1;++i)//枚举0,1
for(int j=0;j<=1;++j){//枚举0,1
int k=bit(i,op[mid],j);//k为 i op j 结果
//left-right结果为k的方案数为 left---mid 结果为 i 的方案数乘以 mid+1---right 结果为 j 的方案数相乘
dp[left][right][k]=(dp[left][right][k]+dp[left][mid][i]*dp[mid+1][right][j]) % 10007;
}
}
return dp[0][n-1][ret];
}
}; //可以动态规划 //经典类型: //0-0 1-1 2-2 3-3 步长为1 //0-1 1-2 2-3 步长为2 //0-2 1-3 步长为3 //0-3 步长为4 //可以先把数字和运算符分别放在A和B中 //dp[i][j][0] i~j中能形成0的有多少种 //dp[i][j][1] i~j中能形成1的有多少种 //0<=a,b<=1 OP=&,|,^ //dp[i][j][a OP b] += (dp[i][k][a] * dp[k + 1][j][b]) % MOD; // i<=k<j; 0<=i,j<=A.size(); OP = B[k] #include <iostream> #include <string> #include <cstring> using namespace std; class Expression { public: const int MOD = 10007; int dp[151][151][2]; int ec(int a, int b, char op){ switch(op){ case '&': return a && b; case '|': return a || b; case '^': return ( (a ^ b) != 0); throw "bad op"; } return 0; } void calc(int i, int j, int k, char op) { for(int a = 0; a < 2; a++) for(int b = 0; b < 2; b++){ int c = ec(a,b,op); dp[i][j][c] += (dp[i][k][a] * dp[k + 1][j][b]) % MOD; dp[i][j][c] %= MOD; } } int countWays(string exp, int len, int ret) { // write code here memset(dp, 0, sizeof(dp)); string A; string B; for (int i = 0; i < len; i++) { if (i & 1) { B += exp.substr(i, 1); } else { A += exp.substr(i, 1); } } for (int i = 0; i < A.size(); i++) for (int k = 0; k < 2; k++) dp[i][i][k] = ((A[i] - '0') == k); for (int step = 2; step <= A.size(); step++) { for (int i = 0; i < A.size(); i++) { int j = i + step - 1; if (j >= A.size()) break; for (int k = i; k < j; k++) { calc(i,j,k, B[k]); } } } return dp[0][A.size() - 1][ret]; } }; #ifdef LOCAL int main() { Expression exp; cout << exp.countWays("1^0|0|1", 7, 0) << endl; } #endif
public static int countWays(String exp, int len, int ret) {
int n=len/2+1;
int[][][] dp=new int[n][n][2];//dp[l][r][p],从l到r范围,可以得到p结果的个数
char[] op=new char[n-1];
int[] num=new int[n];
for(int i=0;i<len-1;i+=2){
num[i/2]=exp.charAt(i)-'0';
op[i/2]=exp.charAt(i+1);
}
num[n-1]=exp.charAt(len-1)-'0';
for(int l=0;l<n;l++){
for(int i=0;i+l<n;i++){
if(l==0){
dp[i][i][num[i]]=1;
dp[i][i][num[i]^1]=0;
}
else{
int n0=0,n1=0;
for(int j=i;j<i+l;j++){
int p00,p01,p11;
p00=(dp[i][j][0]%10007)*(dp[j+1][i+l][0]%10007)%10007;
p01=(dp[i][j][0]%10007)*(dp[j+1][i+l][1]%10007)%10007+(dp[i][j][1]%10007)*(dp[j+1][i+l][0]%10007)%10007;
p11=(dp[i][j][1]%10007)*(dp[j+1][i+l][1]%10007)%10007;
switch(op[j]){
case '|':
n0+=p00;
n1+=p01+p11;
break;
case '&':
n0+=p00+p01;
n1+=p11;
break;
case '^':
n0+=p00+p11;
n1+=p01;
break;
}
}
n0%=10007;
n1%=10007;
dp[i][i+l][0]=n0;
dp[i][i+l][1]=n1;
}
}
}
return dp[0][n-1][ret];
}
import java.util.*;
public class Expression {
public int countWays(String exp, int len, int ret) {
// write code here
int[][][] dp = new int[len][len][2];
final int MOD = 10007;
//枚举长度
for(int l=0; l<len; l+=2){
//枚举起点
for(int i=0; i+l<len; i+=2){
int[] v = {0,0};
if(l==0){
int c = exp.charAt(i) - '0';
dp[i][i][c] = 1;
dp[i][i][c^1] = 0;
}
else{
//枚举中点
for(int j=i; j<i+l; j+=2){
int[] t = new int[3];
t[0] = ((dp[i][j][0]%MOD)*(dp[j+2][i+l][0]%MOD))%MOD;
t[1] = ((dp[i][j][1]%MOD)*(dp[j+2][i+l][0]%MOD) + (dp[i][j][0]%MOD)*(dp[j+2][i+l][1]%MOD))%MOD;
t[2] = ((dp[i][j][1]%MOD)*(dp[j+2][i+l][1]%MOD))%MOD;
char c = exp.charAt(j+1);
if(c == '&'){
v[0] += t[0] + t[1];
v[1] += t[2];
}else if(c == '|'){
v[0] += t[0];
v[1] += t[1] + t[2];
}else if(c == '^'){
v[0] += t[0] + t[2];
v[1] += t[1];
}
v[0] %= MOD;
v[1] %= MOD;
}
dp[i][i+l][0] = v[0];
dp[i][i+l][1] = v[1];
}
}
}
return dp[0][len-1][ret];
}
}