Bzoj4698: [Sdoi2008]Sandy的卡片

本文介绍了一道名为Sdoi2008Sandy的卡片的算法题,通过使用后缀数组及二分查找的方法解决了一个关于收集卡片并寻找共同子串的问题。该题目要求找出能够兑换的最高等级的人物模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bzoj 4698: Sdoi2008 Sandy的卡片

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 13   Solved: 7
[ Submit][ Status][ Discuss]

Description

Sandy和Sue的热衷于收集干脆面中的卡片。然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积
攒卡片兑换超炫的人物模型。每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型
,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人
物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。Sandy的卡片数远
远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片
,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到
哪个等级的人物模型。

Input

第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数
第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+1+Mi个数,用空格分隔,分别表示序列中
的第j个数
n<=1000

Output

一个数k,表示可以获得的最高等级。

Sample Input

2
2 1 2
3 4 5 9

Sample Output

2

Analysis

         标解据说是用kmp,复杂度O(NM^2),我们用后缀数组复杂度能压到(NlogN)
        这道题的最可恶的地方在于没给出“卡片上的数字”的数据规模,我只好假设是在int范围内
        我只会nlogn的后缀数组,因此要用基数排序,也就要用离散
        先把所有的数字读入到一个数组中,中间用互不相同的间隔符隔开
        然后初始化后缀数组
        然后二分k,即串的最长长度,check()就是进行一遍O(N)的扫描,用k分组然后看有没有一组里面包含了来自所有原串的子串(这里我用了一个栈,可以保证均摊时间复杂度是O(N)的)
        完

Code

//Sdoi2008 Sandy的卡片 后缀数组+分组+二分 
#include <cstdio>
#include <cstring>
#include <map>
#define inf (long long)0x7fffffff
#define maxn 1000100
#define ll long long
using namespace std;
ll sa[maxn], rank[maxn], wa[maxn], wb[maxn], ws[maxn], wv[maxn],
   height[maxn], n, card[maxn], hash[maxn], s[maxn], top,
   N, M;
bool vis[maxn];
map<ll,ll> m;
ll read(ll &x)
{
	char c;
	for(c=getchar();c<'0' or c>'9';c=getchar());
	for(x=0;c>='0' and c<='9';c=getchar())x=x*10+c-48;
	return x;
}
void input()
{
	ll i, j;
	read(N);
	for(i=1;i<=N;i++)
	{
		read(M);
		for(j=0;j<M;j++)read(card[n+j]),hash[n+j]=i;
		card[n+M]=inf+i;
		n+=M+1;
	}
	for(i=n-1;i;i--)card[i]=card[i]-card[i-1];
}
bool cmp(ll *r, ll a, ll b, ll l)
{return r[a]==r[b] and r[a+l]==r[b+l];}
void init(ll *r, ll *sa, ll n, ll m)
{
	ll i, j, k=0, p, *x=wa, *y=wb, *t;
	for(i=0;i<m;i++)ws[i]=0;
	for(i=0;i<n;i++)ws[x[i]=r[i]]++;
	for(i=1;i<m;i++)ws[i]+=ws[i-1];
	for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p)
	{
		for(p=0,i=n-j;i<n;i++)y[p++]=i;
		for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
		for(i=0;i<n;i++)wv[i]=x[y[i]];
		for(i=0;i<m;i++)ws[i]=0;
		for(i=0;i<n;i++)ws[wv[i]]++;
		for(i=1;i<m;i++)ws[i]+=ws[i-1];
		for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
		for(t=x,x=y,y=t,i=1,p=1,x[sa[0]]=0;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
	for(i=1;i<n;i++)rank[sa[i]]=i;
	for(i=0;i<n-1;height[rank[i++]]=k)
		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void lisan()
{
	ll cnt=0, i;
	for(i=0;i<n;i++)if(m.find(card[i])==m.end())m[card[i]]=++cnt;
	for(i=0;i<n;i++)card[i]=m[card[i]],M=max(M,card[i]);
}
bool check(const ll k)
{
	ll i;
	for(;top;vis[s[top--]]=false);
	for(i=1;i<=n;i++)
	{
		if(height[i]>=k)
		{
			if(top==0)
			{
				vis[hash[sa[i-1]]]=true;
				s[++top]=hash[sa[i-1]];
			}
			if(!vis[hash[sa[i]]])
			{
				vis[hash[sa[i]]]=true;
				s[++top]=hash[sa[i]];
			}
		}
		else
		{
			if(top==N)return true;
			for(;top;vis[s[top--]]=false);
		}
	}
	return false;
}
ll work()
{
	ll l, r, mid;
	l=1, r=n, mid=(l+r+1)>>1;
	while(l<r)
	{
		if(check(mid))l=mid;
		else r=mid-1;
		mid=(l+r+1)>>1;
	}
	return l;
}
int main()
{
	input();
	lisan();
	init(card,sa,n+1,M+1);
	printf("%lld\n",work()+1);
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值