您的位置:首页 > 其它

后缀数组 【TJOI2013】 bzoj3172 单词

2016-12-28 09:32 323 查看
题目大意:

给定一些单词,这些单词组成一篇文章(单词与单词之间有空格),问每个单词在这篇论文中出现多少次。

题目分析:

可以再单词与单词之间插分隔符,然后用后缀数组来排序,但是排完也没有什么用,并不能用这个序列来做什么。

但是我们发现,前缀相同的后缀一定被排到一块了,所以我们可以利用这个性质来解决问题。

引入一个数组height,height[i]代表排名为i的后缀与排名为i-1的后缀的最长公共前缀。

这个数组又一个性质:height[rnk[i]]>=height[rnk[i-1]]-1

简单的证明一下:

设k为排名为后缀i的排名的前一位的后缀,即k=sa[rnk[i-1]];

设j为k和i的最长公共前缀,即j=height[rnk[i]];

那么我们把k和i的第一位去掉,变成k+1和i+1,那么第i+1和k+1至少有j-1这么长的公共前缀,即height[rnk[i+1]]>=j-1 ;

即height[rnk[i+1]]>=height[rnk[i]]-1;

也就是上面说的height[rnk[i]]>=height[rnk[i-1]]-1。

有了这个性质之后,我们就可以再O(n)的时间内处理出height数组。

一个串的子串都可以表示成这个串的后缀的前缀的形式。我们可以找出以每个单词的首字母为起点的后缀再rnk数组中的位置,这个位置前后的一些后缀会和该后缀有公共前缀(LCP),那么这个公共前缀长度不小于该单词的长度,就说明有该单词被这个后缀包含。

所以问题就转变成了再height数组中以某一个位置为中心,向前后扩展,直到有一个LCP的长度小于单词长度,计算这个区间的长度输出答案。

正解应该是用单调栈维护左最小,右最小,然后每次二分查找。

但是bzoj的数据比较水,暴力查找也能过(这道题我主要练习后缀数组,而且比较懒,就没有写单调栈)

注意事项:没有什么可特别注意的,背好模板,别打错变量。

代码如下:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<iostream>
#define N 1200000
using namespace std;
int n,tot;
char c
,s
;
int sa
,X
,Y
,sum
,rnk
,tmp
,t,hi
,ans[300],len[300];
void get_rank()
{
for(int i=1;i<=tot;i++) sum[s[i]]++;
for(int i=1;i<=127;i++) sum[i]+=sum[i-1];
for(int i=tot;i>=1;i--) tmp[sum[s[i]]--]=i;
for(int i=1;i<=tot;i++)
{
if(i==1 || s[tmp[i]]!=s[tmp[i-1]]) t++;
rnk[tmp[i]]=t;
}
}
void radix_sort(int key[],int order[])
{
for(int i=0;i<=tot;i++) sum[i]=0;
for(int i=1;i<=tot;i++) sum[key[i]]++;
for(int i=1;i<=tot;i++) sum[i]+=sum[i-1];
for(int i=tot;i>=1;i--) tmp[sum[key[order[i]]]--]=order[i];
for(int i=1;i<=tot;i++) order[i]=tmp[i];
}
void get_height()
{
for(int i=1;i<=tot;i++)
{
if(rnk[i]==1) continue;
int j=max(hi[rnk[i-1]]-1,0);int k=sa[rnk[i]-1];
while(s[i+j]==s[k+j]) j++;
hi[rnk[i]]=j;
}
}
void prefix_array()
{
get_rank();
for(int j=1;j<=tot;j<<=1)
{
for(int i=1;i<=tot;i++)
{
X[i]=rnk[i];
Y[i]=i+j>tot?0:rnk[i+j];
sa[i]=i;
}
radix_sort(Y,sa);
radix_sort(X,sa);
t=0;
for(int i=1;i<=tot;i++)
{
if(i==1 || X[sa[i]]!=X[sa[i-1]] || Y[sa[i]]!=Y[sa[i-1]]) t++;
rnk[sa[i]]=t;
}
}
get_height();
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",c);
ans[i]=tot+1;len[i]=strlen(c);
for(int j=0;c[j];j++) s[++tot]=c[j];
s[++tot]=' ';
}
s[tot--]='\0';
prefix_array();
for(int i=1;i<=n;i++)
{
int r=rnk[ans[i]]+1;
while(r<=tot && hi[r]>=len[i]) r++;
r--;
int l=rnk[ans[i]];
while(l>=1 && hi[l]>=len[i]) l--;
l++;
printf("%d\n",r-l+2);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  后缀数组