bzoj P3172 [Tjoi2013]单词

4 篇文章 0 订阅
4 篇文章 0 订阅

传送门

做完这道题终于知道了什么叫做绝望。。

首先,这道题的读入坑到我wa了一页,然后这道题的读入坑了我一晚上,最后这道题的读入吧我玩废了。。

用血写出来的经验最后一个字符要赋'\0'。。

好了说说这道题后缀数组的做法:

把每个单词拼在一起中间由分隔符分开然后做一遍后缀数组,观察sa数组,容易发现如果一个单词包括另一个单词显然要有height[i]大于等于这个单词,如果height[i]小于这单词显然之前或之后的都不包括这个单词,所以二分左右端点+rmq最小,如果区间rmq的height小于这个单词的height就继续二分,但是这道题的n比较小所以rmq卡的跟暴力一样。。


UPDATE(7.19):我们可以理解一下这个RMQ,其实是类似单调栈的思想。

一个小姿势:rmg的手写log2比较快

就是这一段:

log2[0]=-1;
for(int i=1;i<=x;i++)
	log2[i]=log2[i>>1]+1;
代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int Maxn=1100000;
char s[Maxn];
int tax[Maxn],tp[Maxn],sa[Maxn],rank[Maxn],height[Maxn],ks[Maxn],length[Maxn],Min[Maxn][25],log2[Maxn];
int n,x=0,m=256;
inline void Rsort()
{
	for(int i=1;i<=m;i++)
		tax[i]=0;
	for(int i=1;i<=x;i++)
		tax[rank[tp[i]]]++;
	for(int i=1;i<=m;i++)
		tax[i]+=tax[i-1];
	for(int i=x;i>=1;i--)
		sa[tax[rank[tp[i]]]--]=tp[i];
}
inline int cmp(int *f,int x,int y,int w)
{
	return (f[x]==f[y])&&(f[x+w]==f[y+w]);
}
inline void suffix()
{
	for(int i=1;i<=x;i++)
		tp[i]=i,rank[i]=s[i];
	Rsort(); 
	for(int p=1,w=1,i;p<x;w+=w,m=p)
	{
		for(p=0,i=x-w+1;i<=x;i++)
			tp[++p]=i;
		for(i=1;i<=x;i++)
			if(sa[i]>w)
				tp[++p]=sa[i]-w;
		Rsort();
		swap(rank,tp);
		rank[sa[1]]=p=1;
		for(i=2;i<=x;i++)
			rank[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
	}
	
	for(int i=1,j,p;i<=x;height[rank[i]]=j,i++)
		for(j=max(height[rank[i-1]]-1,0),p=sa[rank[i]-1];s[p+j]==s[i+j];j++);
}
inline int rmq(int q1,int q2)
{
	if(q1>q2)
		return 0x3f3f3f3f;
	return min(Min[q1][log2[q2-q1+1]],Min[q2-(1<<log2[q2-q1+1])+1][log2[q2-q1+1]]);
}
inline int L_binary(int rt)
{
	int l=1,r=rank[ks[rt]];
	while(r-l>1)
	{
		int mid=(l+r)>>1;
		if(rmq(mid+1,rank[ks[rt]])>=length[rt])
			r=mid;
		else
			l=mid;
	}
	if(rmq(l+1,rank[ks[rt]])>=length[rt])
		return l;
	else
		return r;
}
inline int R_binary(int rt)
{
	int l=rank[ks[rt]],r=x;
	while(r-l>1)
	{
		int mid=(l+r)>>1;
		if(rmq(rank[ks[rt]]+1,mid)>=length[rt])
			l=mid;
		else
			r=mid;
	}
	if(rmq(rank[ks[rt]]+1,r)>=length[rt])
		return r;
	else
		return l;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1+ks[i-1]+length[i-1]);
		length[i]=strlen(s+1+ks[i-1]+length[i-1]);
		ks[i]=++x;
		x=x+length[i];
		s[x]='@';
	}
	s[x]=0;
	x--;
	suffix();
	height[1]=0;
	log2[0]=-1;
	for(int i=1;i<=x;i++)
		log2[i]=log2[i>>1]+1;
	for(int i=1;i<=x;i++)
		Min[i][0]=height[i];
	for(int j=1;j<=log2[x];j++)
		for(int i=1;i+(1<<j)-1<=x;i++)
			Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]);
	for(int i=1;i<=n;i++)
	{
		int l=L_binary(i);
		int r=R_binary(i);
		printf("%d\n",r-l+1);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值