题目
小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。
A++语言的循环结构如下:
F i x y
循环体
E
其中F i x y表示新建变量 ii(变量 ii 不可与未被销毁的变量重名)并初始化为 xx, 然后判断 ii 和 yy 的大小关系,若 ii 小于等于 yy 则进入循环,否则不进入。每次循环结束后 ii 都会被修改成 i +1i+1,一旦 ii 大于 yy 终止循环。
xx 和 yy 可以是正整数(xx 和 yy 的大小关系不定)或变量 nn。nn 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100100。
“E”表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。
注:本题中为了书写方便,在描述复杂度时,使用大写英文字母“O”表示通常意义下“Θ”的概念。
分析
我们要做的事情是对一组for循环进行时间复杂度的计算,
这些for循环是可以嵌套的,那么for循环的嵌套不就可以看成是一个多叉树嘛。
那么求时间复杂度 => 求多叉树的最大深度。
那么一个树的遍历就搞定了。
但是题目输入的是字符串,我们想要的树,所以先根据题目输入的字符串构造一颗树,与此同时进行语法判断。
构造树那么就需要一个栈来辅助,大致思路就是遍历字符串数组,根据是F 还是 E 来压栈,弹栈,构造树。
与此同时,为了保证可以识别,新建的变量与已经存在但未被销毁的变量重复 我们需要在每个树的节点维护一个集合,来保留当前作用域下出现的变量,同时有一个指向父节点的指针,来递归的判断当前这个变量是否在当前节点之前出现过。
大致思路就是这个样子
代码
#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <set>
using namespace std;
struct line {
string F, i, x, y;
vector<line*> next; // 所有的子节点
line * fa; // 指向父节点的指针
set<string> sympols; // 当前出现的变量
line() : fa(this) {
}
line(line * fa) : fa(fa) {
}
// 判断一个变量是否出现过
bool exist(const string& str) {
// 如果当前节点是根节点,直接返回不存在
if (this->fa == this) return false;
// 如果在当前的变量集合里
if (sympols.find(str) != sympols.end())
return true;
// 不在就递归的判断
return fa->exist(str);
}
// 进行插入一个子节点
bool insert(line* l) {
if (exist(l->i)) return false;
l->fa = this;
l->sympols.insert(l->i);
next.push_back(l);
return true;
}
// 调试用的
friend ostream & operator<< (ostream&out, const line& l){
return out << l.F << " " << l.i << " "
<< l.x << " " << l.y ;
}
};
// 输出树的结构,调试用的。
void output(line* l, int cnt) {
for (int i = 0; i < cnt; i++) {
cout << "-";
}
cout << *l << endl;
for (line* ll : l->next) {
output(ll, cnt + 1);
}
}
// 找到树的最大深度
int dfs(line* root) {
int sum = 0;
for (line* l : root->next) {
int tmp = 0;
if (l->x == "n" && l->y == "n") tmp = 0;
else if (l->x == "n" && l->y != "n") return 0;
else if (l->x != "n" && l->y != "n") {
if (atoi(l->x.c_str()) > atoi(l->y.c_str()))
return 0;
else
tmp = 0;
} else {
tmp = 1;
}
sum = max(sum, tmp + dfs(l));
}
return sum;
}
// 将字符串转换成树型结构,并返回时间复杂度
string judge(line&root, vector<line>&program) {
// 进行编译
int deep = 0;
stack<line*> op;
op.push(&root);
for (size_t i= 0; i < program.size(); i++) {
line*tmp = &program[i];
if (tmp->F == "E") {
if (op.size() == 1) {
deep = -1;
break;
}
op.pop();
} else {
if (op.top()->insert(tmp)) {
op.push(tmp);
} else {
deep = -1;
break;
}
}
}
if (deep == -1 || op.size() != 1) return "ERR";
// output(&root, 0);
deep = dfs(&root);
if (deep == 0) return "O(1)";
else return "O(n^" + to_string(deep) + ")";
}
int main() {
int t, n;
cin >> t;
string result;
for (int i = 0; i < t; i++) {
line root;
root.F = "root";
cin >> n >> result;
vector<line> program(n);
for (int i = 0; i < n; i++) {
cin >> program[i].F;
if (program[i].F == "E") continue;
cin >> program[i].i >> program[i].x >> program[i].y;
}
string res = judge(root, program);
// cout << res << endl;
if (res == "ERR") cout << res << endl;
else if (res == result) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}