2021牛客暑期多校训练营10 A题: Browser Games

A题: Browser Games

原题链接:https://ac.nowcoder.com/acm/contest/11261/A

题目大意

本题的空间限制只有32MB。
n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le 10^5) n(1n105) 个网页游戏在接下来 n n n 天发布,每天发布一个,用户必须用指定url打开才能玩游戏,url长度不超过 100 100 100
但服务器设置有问题,一旦用户以某种方式得到了未发布游戏的url,游戏数据就会泄露。为此决定每天在服务器端设置一组确认前缀,确认前缀是非空字符串,当请求的url与游戏的url相同且至少有一个确认前缀是url的前缀时,服务器才会正确响应,否则将返回未找到游戏。
求出每天所需的最少确认前缀数。

题解

根据题目描述,我们不难发现一个规律:时间越往后,已有的前缀只会变短
我们可以标记每个前缀可用的时间段(区间修改采用差分的方式),但显然直接枚举是不行的,我们可以采取递归:若我们需要查询 1 1 1 ~ k k k 位前缀相同的字符串,我们可以只在 1 1 1 ~ k − 1 k-1 k1 位已经相同的字符串中查找,以减少复杂度。
我们初始可以记录每个游戏发布的时间,然后根据字典序对url进行排序,那么若干位前缀相同的url肯定会构成一个区间,然后每次对于新的一位的相同情况再次划分区间(即分治思想)递归即可(若有区间相同(如 1 1 1 ~ k k k 位均相同的都属于同一个区间,则对 1 1 1 位相同, 1 1 1 ~ 2 2 2 位相同,…不重复进行差分区间修改)的适用前缀则去重)。
详见代码注释(注意区间为左闭右开)

参考代码

#include<bits/stdc++.h>
#define For(i,n,m) for(int i=n;i<=m;i++)
#define FOR(i,n,m) for(int i=n;i>=m;i--)
#define ss(s) scanf(" %s",s)
using namespace std;
void read(int &x){
	int ret=0;
	char c=getchar(),last=' ';
	while(!isdigit(c))last=c,c=getchar();
	while(isdigit(c))ret=ret*10+c-'0',c=getchar();
	 x=last=='-'?-ret:ret;
}

const int MAXN=1e5+5,MAXM=1e2+5;
int n,date[MAXN],ans[MAXN];
char s[MAXN][MAXM];

bool cmp(int a,int b){return strcmp(s[a]+1,s[b]+1)<0;}//字符串字典序比较

void solve(int l,int r,int end,int k){//[l,r)表示在date表中查询的范围,end表示l~r区间中前k-1位相同的最迟date+1(即差分结束位)
	if(l==r)return;//递归边界返回
	int maxn=0;//maxn存储1~k位相同的最迟date(相同前缀的字符串只有全部出现之后,这个前缀才能使用)
	for(int i=l,last=l;i<=r;i++){//last记录当前第k位相同的字符串的区间起始位
		maxn=max(maxn,date[i]);//记录最迟date(注意如果1 ~ k位前缀相同和1 ~ k-1位前缀相同的区间相同,则maxn最终会与end相等,即最终i=r差分修改时会在同一位加减(相当于没有修改),实现自动去重)
		if(i==r||s[date[i]][k]!=s[date[i+1]][k]){//若前缀发生改变(注意i=r也是前缀改变,因为区间[l,r)右开,date[l ~ r-1]与date[r]的1 ~ k-1前缀已经不同)
			ans[maxn]++,ans[end]--;//差分处理(右开区间,差分结束位用end即可)
			solve(last,i,maxn,k+1);//对[last,i)区间继续递归,差分结束位为maxn(maxn已经归入上一行代码的差分区间中了)
			last=i+1,maxn=0;//新区间的起始位last为i+1,重置maxn
		}
	}
}

int main()
{
	read(n);
	For(i,1,n){
		ss(s[i]+1);//读入下标从1开始
		date[i]=i;//标记日期
	}
	sort(date+1,date+n+1,cmp);//根据字符串字典序对date进行排序(排序的目的是使得相似(字典序相近)的字符串的date可以靠的更近(构成区间))
	solve(1,n+1,n+1,1);//solve区间[1,n+1),即[1,n]
	For(i,2,n)ans[i]+=ans[i-1];//差分跑出前缀和
	For(i,1,n)printf("%d\n",ans[i]);
	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值