The 2020 ICPC Asia Yinchuan Regional Programming Contest K. Browser Games

题目大意 : 每天发行一个游戏,当固定前缀里有URL前缀时,这个游戏可以下载,所以我们要添加前缀保证第i天之前的游戏都可以下载(包含URL的前缀),之后的都不能下载(不能包含ULR的前缀)。

思路 : 我们添加的前缀肯定是越来越短的才来保证数量最少(假如有两个前缀有共同前缀,当算到第二个前缀,我们添加一个这两个前缀的公共前缀就行,数量就是1,不是2)

所以我们先用字典树存下来所以的字符串,逆向遍历,当遇到分叉节点时,答案加一,但是如果该分叉路径都已经算过了,那最后一个路径可以用一个前缀表示所有分叉的前缀。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#include<set>
#define first fi
#define second se
#define ios ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
const int N=3e6+10,M=5e4+10;
int n;
char s[M][55];   //  存入字符串 
int son[N][30],fa[N],idx;  //  记录字典树的儿子节点,父节点 ,idx是节点 
int ans[N],ans1[N];  //  该节点有几个儿子 
int fan[N];    //  记录每个字符串最后一位节点 
int res;     //  全局答案 
int insert(char str[])   //  字典树插入 
{
	int p=0;
	for(int i=0;str[i];i++)
	{
		int u=str[i]-'a';
		if(str[i]=='.') u=28;
		if(str[i]=='/') u=29;
		if(!son[p][u]) 
		{
			son[p][u]=++idx;
			fa[idx]=p;
			++ans[p];
		}
		p=son[p][u];
	}
	ans[p]++;  // 末端节点也存为1 
	return p;
}

void query(int x)  //   字典树逆向查询 
{
	while(x)
	{
		ans1[x]++;
		if(ans1[x]==ans[x])//如果凑齐该节点的所有儿子,可以继续向上走,而且可以用一个前缀表示该节点所有儿子的前缀 
		{
			res-=(ans1[x]-1);	// 减去该节点其他所有儿子 的前缀数量,用当前的一个就可以替代 
			x=fa[x];
		}else return ;  //  当没凑齐,就不能继续向上走。 
	} 
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>s[i]+1;
		fan[i]=insert(s[i]+1);  // 记录末端节点 
	}
	
	for(int i=1;i<=n;i++)
	{
		res++;   //  当计算一个字符串,前缀加一 
		query(fan[i]);  //  减去重复的前缀数量 
		cout<<res<<endl;
	}
	
	return 0; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值