题解:P10788 [入门赛 #25] sql
canwen
·
·
题解
题意
简化题意:输入每张表格,并用语句查询表格内容。
$m$ 次 sql 语句查询,格式是 `select [columns] from [table_name] where [header]=x`,输出在 `table_name` 这张表格里,`header` 这一表头的**这一列中**值为 `x` 的那一行的 **`columns` 列的值,且 `columns` 里可能有多个要输出的表头名。**
## 分析
数据范围很小,主要思路是**模拟**。
我们可以定义如下数组。
```cpp
string name[11],title[11][11],excel[11][101][11];
int xx[11],yy[11];
```
以上分别记录每个表格名字,每个表格的表头,每张表格除第一行(也就是除去表头)的内容,每张表格的长度 $x$ 和宽度 $y$。
这样一来,输入的问题就解决了。
如何查询呢?我们可以编写函数分别查找一个表格名字是第几个,一个表头在每张表格里的第几列。
输入字符串,截取每个字符串的有用部分。
可以用 cstring 库中带有的 substr() 函数来进行截取,比较方便。
对于可以输入多个表头名的 `columns`,我们可以记录一下其中逗号出现的位置,这样模拟会比较方便。特殊的,要判断一下没有逗号出现的情况。
完成上述预备环节之后,就可以愉快地枚举了。
可以参考下面的代码。
## Code
```cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
string name[11],title[11][11],excel[11][101][11];//表格名字,每张表的表头,表格整体内容
int xx[11],yy[11],d[150];//每张表的x,y长度 & 逗号出现的位置
int n;//n张表
int f1(string a){
//返回是第几个表格的名字
for(int i=1;i<=n;i++){
if(name[i]==a) return i;
}
}
int f2(int a,string b){
//第a个表中的 b表头的纵坐标
for(int i=1;i<=yy[a];i++){
if(title[a][i]==b) return i;
}
}
void put(int a,int b,int c){
//输出内容
cout<<excel[a][b][c]<<" ";
return;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>name[i];//输入表格名字
int x,y;cin>>x>>y;
for(int j=1;j<=y;j++) cin>>title[i][j];//先单独输入表头在title数组,方便判断
x--,xx[i]=x,yy[i]=y; //除去表头这一行,剩下 x-1 行
for(int j=1;j<=x;j++){
for(int k=1;k<=y;k++){
cin>>excel[i][j][k];
}
}
}
int m;cin>>m;
for(int i=1;i<=m;i++){
//select [columns] from [table_name] where [header]=x
string out;cin>>out>>out;//记录 `columns`
int len=0;//记录多少个逗号
memset(d,0,sizeof(d));//初始化
for(int j=0;j<out.size();j++)
if(out[j]==',') d[++len]=j;
string which;cin>>which>>which;//记录 `table_name`
int num=f1(which);//调用函数查询是第几个表格的名字
string tmp;cin>>tmp>>tmp;
int tmp1=tmp.find('=');/*查找等号出现的位置*/
string pd1=tmp.substr(0,tmp1),pd2=tmp.substr(tmp1+1);//截取
int num2=f2(num,pd1);//查询表头是第几列,方便枚举
for(int j=1;j<=xx[num];j++){
if(excel[num][j][num2]==pd2){//找到了符合条件的
if(len==0){
//特判单个表头名的
put(num,j,f2(num,out));
}else{
for(int k=1;k<=len+1;k++){
string awa;
if(k==1) awa=out.substr(0,d[k]);
else if(k==len+1) awa=out.substr(d[k-1]+1);
else awa=out.substr(d[k-1]+1,d[k]-d[k-1]-1);
put(num,j,f2(num,awa));
//上述是推出来的截取计算式
//k==1 0,d[k]
//k==2 d[k-1]+1,d[k]-d[k-1]-1
//k==len+1 d[k-1]+1
}
}
printf("\n");
}
}
}
return 0;//good habit~
}
```
下面补充一下上面的代码中出现的在 cstring 库中的函数的使用方法。
```cpp
#include<cstring>
#include<iostream>
using namespace std;
int main(){
string a="abcd";
//a.substr(x,y) 截取字符串 a 从下标 x 位置开始,连续的 y 个字符
cout<<a.substr(0,2)<<endl;//输出 `ab`
cout<<a.substr(1)<<endl;//若没有传进 y,默认从下标 x 位置开始截取到末尾,输出 `bcd`
//a.find(x) 返回 x 在 a 中的位置(下标从0开始)
cout<<a.find('d')<<endl;//输出3
return 0;
}
```