HDU 6194 string string string :后缀数组+单调队列 | 后缀自动机
来源:互联网 发布:淘宝买游戏账号被骗 编辑:程序博客网 时间:2024/05/29 13:04
题意:给出一个字符串,求出出现了恰好k次的子串的个数。
题解:恰好k次 = 至少k次 - 至少k+1次。答案转化为求至少出现k次的子串个数统计。构造好后缀数组以及很重要的Height数组之后。用一个k-1的窗子去滑动。窗子里边放着k-1个Height值(Height[ i+1 ],Height[ i+2 ],……,Height[ i+k ]),这样k-1个值就联系了k个后缀(SA[ i ],SA[ i+1 ],……,SA[ i+k ]),显然要求出这k-1个Height值的最小值,这个最小值就是这k个后缀的极大公共前缀了,假设是x,那么长度为1..x的前缀都在这个窗子里面出现了k次,也就是说他们都是符合答案的子串了。但是要考虑前边已经重复统计过了1..x中的一部分,考虑前一个窗子(Height[ i ],Height[ i+1 ],……,Height[ i+k-1 ]),讨论前面这个窗子的最小值y:1、y<x,那么说明上一个窗子的最小值一定是Height[ i ] ,因此上一个窗子统计了1..Height[ i ]部分,这一次需要统计Height[ i ]..x部分。2、y>x,那么就是说明前面窗子的极大公共前缀长度 比 这一个窗子的 极大公共前缀长,而这两个窗子有(i+1,i+2,,……,i+k-1)这k-2个元素是一样的。因此考虑所有的k个值(Height[ i ],Height[ i+1 ],……,Height[ i+k-1 ],Height[ i+k ]),1..x必然是他们的公共前缀,那么由于y>x,前面一个窗子已经统计完了1..y的部分,1..x的部分被计算在前一个窗子里面了,这一次不需要计算,为了和上边一个式子统一起来,我们考虑Height[ i ],显然有Height[ i ]>=y>x,进而有Height[ i ]>x。
因此结论是:用k-1的窗子从2开始滑动,维护窗子的最小值,然后每个窗子和刚刚移出窗子的那个Height作比较,如果min>Height,就统计min-Height。相反不统计。维护最小值用单调队列复杂度最低(priority_queue是nlogn,手写数组模拟是n)。构造后缀数组用倍增方法,复杂度是nlogn,因此整体复杂度是nlogn。
PS:CSDN上很火的一个五分钟学后缀数组的博客,代码是错误的,只需要把我下边这个AC的代码的统计答案部分移植到那个版本的代码中去,然后提交到HDU 6194就可以愉快的得到一个WA。假博客坑了我两整天的时间。但是我也没有找到那个代码具体错在哪里,感觉可能是他的基数排序比较丑,把自己弄挂了(第二关键字排序之后,肯定是n个位置都有一个序号,但如果在中间加入一个assert(p==n)就可以0ms得到WA,所以他的第二关键字排序有问题)。自己也是拿这道题入门的后缀数组,都怪自己太菜……
Code:
#include<bits/stdc++.h>using namespace std;typedef long long ll;const int MaxN=1e5+100;const int MAXN = MaxN;int cntA[MaxN],cntB[MaxN],tsa[MAXN],A[MAXN],B[MAXN];int sa[MAXN],Rank[MAXN],h[MAXN];char ch[MAXN];struct Node{int val,index;Node(int val_,int index_):val(val_),index(index_){}bool operator < (const Node b)const{if (val==b.val){return b.index<index;}return b.val<val;}};priority_queue<Node>pq;void GetSa(char *ch,int *sa,int *rank,int n){ for(int i=0;i<MaxN;i++) cntA[i]=0; for(int i=1;i<=n;i++) cntA[ch[i]]++; for(int i=1;i<=MaxN;i++) cntA[i]+=cntA[i-1]; for(int i=n;i;i--) sa[cntA[ch[i]]--]=i; rank[sa[1]]=1; for(int i=2;i<=n;i++){ rank[sa[i]]=rank[sa[i-1]]; if(ch[sa[i]]!=ch[sa[i-1]]) rank[sa[i]]++; } for(int l=1;rank[sa[n]]<n;l<<=1){ for(int i=0;i<MaxN;i++) cntA[i]=0; for(int i=0;i<MaxN;i++) cntB[i]=0; for(int i=1;i<=n;i++){ cntA[A[i]=rank[i]]++; cntB[B[i]=(i+l<=n)?rank[i+l]:0]++; } for(int i=1;i<MaxN;i++) cntB[i]+=cntB[i-1]; for(int i=n;i;i--) tsa[cntB[B[i]]--]=i; for(int i=1;i<MaxN;i++) cntA[i]+=cntA[i-1]; for(int i=n;i;i--) sa[cntA[A[tsa[i]]]--]=tsa[i]; rank[sa[1]]=1; for(int i=2;i<=n;i++){ rank[sa[i]]=rank[sa[i-1]]; if(A[sa[i]]!=A[sa[i-1]] || B[sa[i]]!=B[sa[i-1]]) rank[sa[i]]++; } }}void GetHeight(char *ch,int *sa,int *rank,int *height,int n){ GetSa(ch,sa,rank,n); for(int i=1,j=0;i<=n;i++){ if(j) j--; while(ch[i+j]==ch[sa[rank[i]-1]+j]) j++; height[rank[i]]=j; }}int GetK(int k,int n){ int ans=0; k--; if(k==0){ for(int i=1;i<=n;++i) ans=ans+(n-sa[i]+1-h[i]); return ans; }while (!pq.empty())pq.pop();for (int i=2;i<=n;i++){while (!pq.empty()&&pq.top().index<i-k+1)pq.pop();pq.push(Node(h[i],i));if (i>k){int top = pq.top().val;int last = h[i-k];ans +=max(0,top-last);}} return ans;}void Run(){ int n,k; scanf("%d",&k); scanf("%s",ch+1); n=strlen(ch+1); GetHeight(ch,sa,Rank,h,n); printf("%d\n",GetK(k,n)-GetK(k+1,n));}int main(){ int T; scanf("%d",&T); while(T--){ Run(); } return 0;}
- HDU 6194 string string string :后缀数组+单调队列 | 后缀自动机
- HDU 6194 string string string [后缀数组]
- HDU 6194 string string string【后缀数组】
- string string string hdu 6194 (后缀数组做法)
- HDU 6194 string string string 后缀数组+lcp、Two Pointers
- HDU 6194 string string string 后缀数组+rmq
- hdu 6194 string string string 后缀数组+rmq+容斥
- 【后缀自动机】[HDU 4641]K-string
- hdu 5008 Boring String Problem(后缀自动机构造后缀树)
- hdu4641-K-string(后缀自动机)
- hdu6194 string string string 后缀数组 + RMQ
- hdu 3553 Just a String (后缀数组)
- hdu 5008 Boring String Problem(后缀数组)
- HDU 5008 Boring String Problem 后缀数组
- HDU 5030 Rabbit's String 后缀数组
- hdu 5030 Rabbit's String(后缀数组)
- hdu 5008 Boring String Problem(后缀数组)
- hdu 5008 Boring String Problem 【后缀数组】
- Leetcode--Two Sum
- 深度学习--Batch_Normlization
- day3代码
- “RPC好,还是RESTful好?”
- 牛客网-剑指offer-10-矩形覆盖
- HDU 6194 string string string :后缀数组+单调队列 | 后缀自动机
- 微信小程序8 界面api
- Project facet Java version 1.8 is not supported.
- 无穷小放飞互联网,赶超美日不是梦
- 最新pycharm破解(亲试有效)
- 多线程
- SpringBoot 使用fastjson初识
- PyQt5介绍
- 如何在其他项目调用webservice 发布的soap风格的接口