禁止的前缀
解题思路
这道题目要求我们维护两个字符串集合X和Y,并实时计算Y中没有以X中任何字符串为前缀的字符串数量。核心思路是使用字典树(Trie)来高效处理字符串前缀问题:
-
字典树结构:用于存储所有字符串,快速查询前缀关系
-
两种操作:
-
操作1(插入X):标记X中的字符串路径,并更新受影响Y字符串的计数
-
操作2(插入Y):检查是否已有X的前缀,若无则增加计数
-
-
实时维护:每次操作后立即输出当前满足条件的Y字符串数量
#include<bits/stdc++.h> using namespace std; const int N = 5e5 + 10; // 字典树相关变量 int t[N][26]; // 字典树结构,每个节点有26个子节点(a-z) int cnt[N]; // 记录以该节点结尾的Y字符串数量 int id; // 当前字典树节点ID int vis[N]; // 标记是否为X中某个字符串的结束节点 int ans; // 当前满足条件的Y字符串总数 // 操作1:将字符串s插入X集合 void ins1(string s) { int p = 0; // 从根节点开始 for(int i = 0; i < s.size(); i++) { int x = s[i] - 'a'; // 当前字符对应的索引 if(t[p][x] == 0) t[p][x] = ++id; // 创建新节点 p = t[p][x]; // 移动到子节点 if(vis[p]) break; // 如果已标记为X的前缀,提前终止 } // 如果当前路径未被标记为X的前缀 if(!vis[p]) { vis[p] = 1; // 标记为X的前缀 int d = cnt[p]; // 获取该节点下Y字符串的数量 int q = 0; // 重新从根节点开始 ans -= d; // 这些Y字符串不再满足条件 // 更新路径上的计数 for(int i = 0; i < s.size(); i++) { int x = s[i] - 'a'; cnt[q] -= d; // 减少路径上的计数 q = t[q][x]; // 移动到子节点 } } } // 操作2:将字符串s插入Y集合 void ins2(string s) { int p = 0; for(int i = 0; i < s.size(); i++) { int x = s[i] - 'a'; if(t[p][x] == 0) t[p][x] = ++id; p = t[p][x]; if(vis[p]) break; // 如果遇到X的前缀,提前终止 } // 如果当前字符串没有X的前缀 if(!vis[p]) { ans++; // 满足条件的Y字符串+1 cnt[p]++; // 记录在该节点结束的Y字符串数量 int q = 0; // 从根节点开始 // 更新路径上的计数 for(int i = 0; i < s.size(); i++) { int x = s[i] - 'a'; cnt[q]++; // 增加路径上的计数 q = t[q][x]; } } } int main() { int n; cin >> n; // 查询数量 while(n--) { int op; // 操作类型 string s; // 操作字符串 cin >> op >> s; if(op == 1) ins1(s); // 插入X else ins2(s); // 插入Y cout << ans << endl; // 输出当前结果 } return 0; }

浙公网安备 33010602011771号