[CSP-J 2022] 逻辑表达式
题目
洛谷题库
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+1;
string s;
int ki,//第几个左括号 k1[N],//第几个左括号的位置k2[N],//(关键)每个左括号(哪个位置)对应右括号的位置and_n,//&短路数 or_n,//|短路数 ans;
int find(int i,int r,char x){//块内找到第一个x运算符 while(s[i]!=x&&i<=r){//找不到运算符就到块外 if(s[i]=='(')i=k2[i]+1;//绕过括号,使得最后能分割出完整的块号 else i++;}return i;
}
//递归,大规模拆分成小规模。
bool run(int l,int r,char c='|'){//先以'|'拆分成子辈块 while(k2[l]==r)l++,r--,c='|';//括号块,消去外括号。括号内从|拆分 if(l==r)return s[l]-'0';//递归出口,该块只剩数int m1=find(l,r,c);/*找到第一个运算符c,拆分成单块+剩余两部分。如算式1&a&b&c&d中的1就是单块,剩下的a&b&c&d是剩余部分 前部分,子辈块(以'|'分割的),还得递归继续用'&'分割孙辈('&'分割的),可以算出结果后部分要递归拆分出各单块*/ bool k=run(l,m1-1,'&');//以'&'拆分,选出孙辈诸块 for(int m2;m1<r;m1=m2){//逐个拆分剩余块,先是'\'分隔得,再'&'分割得m2=find(m1+1,r,c);//找到剩余部分第一个运算符位置,逐个选出第一块if(!k&&s[m1]=='&')and_n++;//前块和运算符已形成短路 else if(k&&s[m1]=='|')or_n++;//和后块无关else k=run(m1+1,m2-1);//前面没形成短路,结果只跟后块有关 }return k;
}
int main(){//freopen("expr.in","r",stdin);cin>>s;s=' '+s;//k2[0]=0,就是说0位置的(的)默认在0位置。死循环 for(int i=1;s[i];i++)if(s[i]=='(')k1[ki++]=i;//仅用来暂时标识左括号 else if(s[i]==')')k2[k1[--ki]]=i;//记住每个左括号对应右括号的位置 cout<<run(1,s.length()-1)<<endl;/*递归分块解决。
先一层全部以'\'分割,叫子块
可能会剩完整括号块,或者单数据
再一层全部以'&'分割,叫孙块
可能会剩完整括号块,或者单数据
*/cout<<and_n<<" "<<or_n;return 0;
}
思路
1.记住每个左括号对应右括号的位置
2.在当前块中分阶段找到第一个运算符,将整块分成第一块和剩余部分
3.第一层优先级低,用|逐个拆分成各个子块
每次拆分成第一块和剩余
4.第二层优先级高,用&逐个拆分成各个孙块
每次拆分成第一个和剩余
5.拆分得只剩括号,先消括号
6.拆分得数,直接返回结果(递归出口)
7.递归求第一部分得值
8.如果短路,直接返回值,
否则,直接计算并返回剩余部分值
9.基本上每个字符会处理常数次,时间复杂度O(n)。