

题解
实现方式:
-
将字符串映射为数字值,并将字符串间的嵌套关系视为图结构。对存在间接赋值关系的语句执行加边操作,最终查询操作通过一次DFS完成。
-
图结构采用链式前向星存储方式,与常规前向星的区别在于不记录边权值,而是记录点权值。每个节点的长度值等于其自身权值加上所有子节点权值的总和。
-
使用getline配合stringstream读取字符串数据,注意在调用getline前需执行getchar或ignore操作以清除缓冲区。
具体流程:
- 输入
- 直接赋值:
- 删除所有连接边(h[x]=-1),点权归零。
- 如果为‘$’开头,dfs计算其权重,并将权重加入点权中。
- 不为‘$’开头,直接将字符串长度加入点权中。
- 间接赋值:
- 删除所有接边(h[x]=-1),点权归零。
- 如果为‘$’开头,加边。
- 不为‘$’开头,点权加上该字符串长度。
- 查询:dfs查询即可。
优化:
记忆化搜索,注意是在每一轮内部记忆化,即在每一轮开始的时候将vis数组归零,如果全局记忆化会有神奇的影响导致答案不对。
代码
有注释版本(ai注释):
#include <bits/stdc++.h>
using namespace std;
/* ---------------- 全局常量 ---------------- */
const long long mod = 1e9 + 7; // 取模值
const int N = 4'001'000; // 预估最大节点数
const int M = 4'001'000; // 预估最大边数
/* ---------------- 全局变量 ---------------- */
int n, op, cnt = 0; // n: 操作次数 op: 当前操作类型 cnt: 节点计数器
unordered_map<string, int> mp; // 字符串 -> 节点编号的映射
/* 邻接表存图 */
int idx = 0; // 当前边的编号
int h[N], e[M], ne[M]; // h: 头指针 e: 终点 ne: next 边
long long w[N]; // w[i] 表示节点 i 的“基础值”
long long vis[N]; // 记忆化数组:vis[i] 表示以 i 为根的答案
/* ---------------- 工具函数 ---------------- */
inline void add(int x, int y) { // 加一条 x -> y 的边
e[idx] = y, ne[idx] = h[x], h[x] = idx++;
}
// 把字符串映射成节点编号;不存在则新建
inline int dis(string s) {
if (!mp.count(s)) return mp[s] = ++cnt;
return mp[s];
}
// 记忆化搜索:求以 x 为根的“答案”
inline long long query(int x) {
if (vis[x]) return vis[x]; // 已经算过则直接返回
long long res = w[x]; // 先累加自己的基础值
for (int i = h[x]; i != -1; i = ne[i]) {
int j = e[i];
res += query(j); // 递归累加所有孩子的答案
res %= mod;
}
vis[x] = res; // 记忆化
return res;
}
/* ---------------- 主程序 ---------------- */
int main() {
memset(h, -1, sizeof h); // 初始化邻接表头指针
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
getchar(); // 吃掉第一行的换行
while (n--) {
memset(vis, 0, sizeof vis); // 每组操作前清空记忆化数组
string s;
getline(cin, s); // 读入整行
stringstream ss(s);
ss >> op; // 解析操作类型
if (op == 1) { // 操作 1:重新定义一个 key 的内容
string key, temp;
ss >> key; // 变量名
int t = dis(key); // 获取节点编号
long long res = 0; // 累加器
// 逐个解析后面的片段
while (ss >> temp) {
if (temp[0] != '$') { // 普通字符串
res += temp.size();
res %= mod;
} else { // 引用变量
int id = dis(temp.substr(1)); // 去掉 '$' 后拿到变量名
res += query(id); // 递归求值
res %= mod;
}
}
w[t] = res; // 更新基础值
h[t] = -1; // 操作 1 不允许孩子,因此清空邻接表
} else if (op == 2) { // 操作 2:重新定义一个 key 的内容(允许引用)
string key, temp;
ss >> key;
int p = dis(key);
w[p] = 0; // 重置基础值
h[p] = -1; // 先清空所有孩子
while (ss >> temp) {
if (temp[0] != '$') { // 普通字符串
w[p] += temp.size();
w[p] %= mod;
} else { // 引用变量
int id = dis(temp.substr(1));
add(p, id); // 加一条 p -> id 的边
}
}
} else if (op == 3) { // 操作 3:查询
string key;
ss >> key;
cout << query(dis(key)) << '\n';
}
}
return 0;
}
无注释版本:
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7,N=4001000,M=4001000;
int n,op,cnt;
unordered_map<string,int>mp;
int idx,h[N],e[M],ne[M];
long long w[N];
long long vis[N];
inline void add(int x,int y){
e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}
inline int dis(string s){
if(!mp.count(s))return mp[s]=++cnt;
return mp[s];
}
inline long long query(int x){
if(vis[x])return vis[x];
long long res=w[x];
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
res+=query(j);
res%=mod;
}
vis[x]=res;
return res;
}
int main(){
memset(h,-1,sizeof h);
cin>>n;
getchar();
while(n--){
memset(vis,0,sizeof vis);
string key,value,temp,s;
getline(cin,s);
stringstream ss(s);
ss>>op;
if(op==1){
ss>>key;
int t=dis(key);
long long res=0;
while(ss>>temp){
if(temp[0]!='$'){
res+=temp.size();
res%=mod;
}else{
int t=dis(temp.substr(1));
res+=query(t);
res%=mod;
}
}
w[t]=res;
h[t]=-1;
}else if(op==2){
ss>>key;
int p=dis(key);
w[p]=0;
h[p]=-1;
while(ss>>temp){
if(temp[0]!='$'){
w[p]+=temp.size();
w[p]%=mod;
}else{
int t=dis(temp.substr(1));
add(p,t);
}
}
}else if(op==3){
ss>>key;
cout<<query(dis(key))<<endl;
}
}
}