后缀数组 【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的数据比较水,暴力查找也能过(这道题我主要练习后缀数组,而且比较懒,就没有写单调栈)
注意事项:没有什么可特别注意的,背好模板,别打错变量。
代码如下:
给定一些单词,这些单词组成一篇文章(单词与单词之间有空格),问每个单词在这篇论文中出现多少次。
题目分析:
可以再单词与单词之间插分隔符,然后用后缀数组来排序,但是排完也没有什么用,并不能用这个序列来做什么。
但是我们发现,前缀相同的后缀一定被排到一块了,所以我们可以利用这个性质来解决问题。
引入一个数组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; }
相关文章推荐
- [AC自动机 fail树 || 后缀数组] BZOJ 3172 [Tjoi2013]单词
- BZOJ 题目3172: [Tjoi2013]单词(AC自动机||AC自动机+fail树||后缀数组暴力||后缀数组+RMQ+二分等五种姿势水过)
- [BZOJ]3172: [Tjoi2013]单词 AC自动机(或广义后缀自动机)
- BZOJ 3172 [Tjoi2013]单词 后缀自动机
- bzoj 3172 [Tjoi2013]单词 后缀自动机
- 【BZOJ】3172: [Tjoi2013]单词(后缀自动机)
- BZOJ_3172_[Tjoi2013]单词_后缀自动机
- BZOJ 3172: [Tjoi2013]单词 AC自动机/后缀自动机
- BZOJ 3172 [Tjoi2013]单词 AC自动机(fail树)
- [AC自动机][fail树][BZOJ 3172][TJOI2013]单词
- BZOJ3172 [Tjoi2013]单词(AC自动机+打标记)
- 【TJOI2013】【BZOJ3172】单词
- 【AhoCorasickAutoMata】bzoj3172[Tjoi2013]单词
- [BZOJ3172]TJOI2013单词|AC自动机
- 【BZOJ】【3172】【TJOI2013】单词
- bzoj 3172 [Tjoi2013]单词(fail树,DP)
- 【BZOJ 3172】 [Tjoi2013]单词
- BZOJ 3172 Tjoi2013 单词 后缀数组
- [省选前题目整理][BZOJ 3172][TJOI 2013]单词(AC自动机+fail树)
- BZOJ 3172([Tjoi2013]单词-后缀数组第一题+RMQ)