K. Browser Games
银川站的K题。第一次写真题,浅浅发个题解记录一下
题目大意:
游戏公司要发布n个游戏,一天发布一个,每个游戏由一个字符串构成,由于游戏公司的一些不当操作。游戏可以通过游戏名前缀进行访问,导致可以利用bug把没发布的游戏全部偷出来,现在老板让你在规划每一天所需要的前缀的最小值,且前i天的前缀不能访问到没发布的游戏
样例:
解释一下样例吧:第一天毫无疑问选u作为前缀可以最小,且不访问到后面的。第二天需要 u hs (为什么不h呢?因为如果以h为前缀就可以访问到第三天的),第三天 只需要 u h(h可以直接访问到2,3的游戏)
思路:
这么多字符串的前缀,考虑Tire树来处理,但首先我们需要算一下内存5万5028*4/1024/1024差不多有300MB,题目给的512MB所以够用 ,首先我们把所有字符串读入然后进行Tire树的初始化。
考虑动态的处理数据,容易想到如果想要前缀数量变小,必须让后面加进来的前缀是前面前缀的前缀(比如说 h 是hs的前缀)这样就可以把前面的前缀给去掉了。但是要注意的地方是,后面要加进来的前缀必须满足后面所有的字符串都没有这个前缀,然后我们把以前的前缀剪掉即可
代码:有一些小注释:
// An highlighted block
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
using namespace std;
#define rall(x) (x).rbegin(), (x).rend()
#define all(x) (x).begin(), (x).end()
#define pb push_back
#define sz(a) (int) (a).size()
const int N=3*1e6+10;
int son[N][29],cnt[N],idx;
int n,ans;
int bf[N];//记录以前的分支数量
int getnum(char op){
if(op>='a'&&op<='z') return op-'a';
else if(op=='.') return 26;
else return 27;
}
void insert(string s){
int p=0;
for(int i=0;s[i];i++){
int u=getnum(s[i]);
if(!son[p][u]) son[p][u]=++idx;
p=son[p][u];
cnt[p]++;
}
}
void find(string s){ //重点核心
int p=0;
vector<int> m;
for(int i=0;s[i];i++){
int u=getnum(s[i]);
cnt[son[p][u]]--;
if(!cnt[son[p][u]]){//如果已经是当前前缀的最后一个字符串;
int x=1-bf[son[p][u]];//因为它已经是满足条件的最优解,所以把以前的前缀全部删掉
ans+=x;//答案加上变化
for(auto d:m){ //遍历他的所有父节点把变化加上 因为父节点的变化就是由各个子节点变过来的
bf[d]+=x;
}
return ;
}
//如果没有,就记录这条路径上的所有父节点,等最后处理;
p=son[p][u];
m.pb(p);
}
}
void solved() {
cin>>n;
vector<string> a(n);
for(int i=0;i<n;i++){
cin>>a[i];
insert(a[i]);
}
for(int i=0;i<n;i++){
find(a[i]);
cout<<ans<<endl;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
solved();
return 0;
}