本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【ICPC】2022银川站 K. Browser Games | 贪心、Trie 树
题目链接
题目
In the upcoming n days, n browser games will be released on a new website. According to the plan, the administrator will release a new game per day. Users have to open the corresponding URL (Uniform Resource Locator) and get feedback from the server to download a game.
However, the setup of the server uses unreadable legacy codes. Once a user somehow finds the URL of an unreleased game, the data of the game would leak out. To temporarily fix the problem, the administrator decided to add a series of confirmation prefixes, which are non-empty strings, at the server-side. The server will respond with the correct game data when the requested URL does correspond to a game (no matter released or unreleased) and at least one confirmation prefix is a prefix of the URL; otherwise, the server will declare that the game is not found.
To make the work easier, the administrator asks you to find the minimum number of confirmation prefixes the server required to avoid data leaks every time after a new game release.
题目大意
在接下来的 天里,管理员将每天在一个新网站上发布发布一款新游戏。用户必须打开相应的 URL(统一资源定位器)并从服务器获得反馈才能下载游戏。
但是,服务器的设置使用了不可读的旧代码。一旦用户以某种方式找到未发布游戏的 URL,若服务器对其进行响应,该游戏的数据就会泄露出去。为了暂时解决这个问题,管理员决定在服务器端添加一系列非空字符串的确认前缀。当所请求的 URL 确实对应于游戏(无论是发布的还是未发布的)并且至少一个确认前缀是 URL 的前缀时,服务器将进行响应;否则,服务器将声明未找到游戏。
管理员要求您找到每次新游戏发布后服务器所需的最小确认前缀数,以避免数据泄漏。
思路
为了最小化确认前缀数,我们想要选择尽可能短的确认前缀,来覆盖尽可能多的已发布的 URL,而不能覆盖到未发布的 URL。
首先用输入的所有字符串建一颗 Trie 树,每个字符串 的最后一个字符在 Trie 树上对应的节点为 。Trie 树上需要额外记录以下信息:
- :最后一个访问该节点的字符串是第 天发布的游戏的 URL。
- :该节点的父节点。
- :标记。
这些额外信息有什么用处呢?
我们枚举第 天发布的 URL,令 从 开始向根节点跳,当 的父节点的 不等于 时停止向上跳,则此时从根节点到 路径上经过的字符串就是覆盖第 天发布的 URL 而不覆盖到未发布的 URL 的最短的前缀。
我们让 父节点的 增加 1,这意味着如果我们使用了节点 的父节点的前缀作为确认前缀,顺便也可以把字符串 给覆盖掉。同理我们选择以节点 为结尾的确认前缀,也顺便覆盖了所有使从 到 的路径上的节点的 属性增加的字符串。
所以我们的选择会让第 个游戏发布后服务器所需的最小确认前缀数在 的基础上加上 。依次计算输出即可。
代码
#include <bits/stdc++.h>
using namespace std;
struct asdf{
int nxt[28],cnt,flg,fa;
}tre[2500001];
int n,tot=1,adr[50001];
char ch;
int main()
{
scanf("%d",&n);
for (int t,i=1;i<=n;++i)
{
for (ch=getchar();!((ch>='a'&&ch<='z')||(ch=='/'||ch=='.'));ch=getchar());
for (t=1;((ch>='a'&&ch<='z')||(ch=='/'||ch=='.'));ch=getchar())
{
int x;
if (ch>='a'&&ch<='z') x=ch-'a';
if (ch=='/') x=26;
if (ch=='.') x=27;
if (!tre[t].nxt[x])
{
tre[t].nxt[x]=++tot;
tre[tot].fa=t;
}
t=tre[t].nxt[x];
tre[t].flg=i;
}
adr[i]=t;
}
int ans=0,cnt;
for (int i=1,j;i<=n;++i)
{
cnt=0;
for (j=adr[i];tre[tre[j].fa].flg==i;j=tre[j].fa)
cnt+=tre[j].cnt;
cnt+=tre[j].cnt;
tre[tre[j].fa].cnt++;
ans=ans+1-cnt;
printf("%d\n",ans);
}
return 0;
}