P5341 [TJOI2019]甲苯先生和大中锋的字符串(后缀数组)
发布日期:2021-06-30 10:30:19 浏览次数:3 分类:技术文章

本文共 2479 字,大约阅读时间需要 8 分钟。

求出现次数恰好为 k k k的所有子串中,出现次数最多的长度


这个题用 S A M SAM SAM就太赖皮了,每个点的 s i z siz siz值就是出现i+的次数…

所以这里用后缀数组来写

尺取一个长度为 k − 1 k-1 k1 h e i g h t height height区间,设区间最小值是 x x x,尺取区间是 [ i − k + 2 , i ] [i-k+2,i] [ik+2,i]

那么说明在 i ∈ [ i − k + 1 , i ] i\in[i-k+1,i] i[ik+1,i]的所有 s a [ i ] sa[i] sa[i]中,都出现过长 x x x的这个前缀

如果 x x x不出现在 s a [ i − k ] sa[i-k] sa[ik] s a [ i + 1 ] sa[i+1] sa[i+1],就是恰好出现 k k k

所以合法长度的下界是 m a x ( h e i g h t [ i − k + 1 ] , h e i g h t [ i + 1 ] ) max(height[i-k+1],height[i+1]) max(height[ik+1],height[i+1])

合法长度的上界是 x x x

问题就得到解,我们只需要知道所有区间长度为 k − 1 k-1 k1 h e i g h t height height区间最小值即可

这个可以 S T ST ST表预处理,但是单调队列可以做到 O ( n ) O(n) O(n)??

最后加一句所有写后缀数组的人都会口胡的话,使用 D C 3 DC3 DC3法预处理 h e i g h t height height数组整体复杂度达到 O ( n ) O(n) O(n)

#include 
using namespace std;const int maxn = 4e5+10;int height[maxn],sa[maxn],x[maxn],rk[maxn],y[maxn],c[maxn],n,m,k,cha[maxn];char a[maxn];void SA(){
m = 500; int las = max(n,m); for(int i=0;i<=las+1;i++) sa[i]=rk[i]=x[i]=y[i]=c[i]=height[i]=0; for(int i=1;i<=n;i++) c[x[i]=a[i]]++; for(int i=1;i<=m;i++) c[i] += c[i-1]; for(int i=n;i>=1;i--) sa[c[x[i]]--] = i; for(int k=1;k<=n;k<<=1) {
int num = 0; for(int i=n-k+1;i<=n;i++) y[++num] = i; for(int i=1;i<=n;i++) if( sa[i]>k ) y[++num] = sa[i]-k; for(int i=0;i<=m;i++) c[i] = 0; for(int i=1;i<=n;i++) c[x[i]]++; for(int i=1;i<=m;i++) c[i] += c[i-1]; for(int i=n;i>=1;i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0; swap(x,y); num = 1, x[sa[1]] = 1; for(int i=2;i<=n;i++) x[sa[i]] = ( y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k] )?num:++num; if( num==n ) break; m = num; } for(int i=1;i<=n;i++) rk[sa[i]] = i; for(int i=1,k=0;i<=n;i++) {
if( rk[i]==1 ) continue; if( k ) k--; int j = sa[rk[i]-1]; while( i+k<=n&&j+k<=n&&a[i+k]==a[j+k] ) k++; height[rk[i]] = k; }}int head,tail,q[maxn];void init1(){
for(int i=2;i<=n;i++) {
int l = max(height[i],height[i+1]), r = n-sa[i]+1; if( l
=head&&height[q[tail]]>=height[i] ) tail--; q[++tail] = i; if( i<=k ) continue; //现在排除一些元素,使得区间长度小于等于k while( i-q[head]+1>k ) head++; int x = height[q[head]], mx = max( height[i-k],height[i+1] );//[i-k+1,i] if( mx
> t; while( t-- ) {
scanf("%s%d",a+1,&k); n = strlen( a+1 ); SA(); if( k==1 ) init1(); else init2(); for(int i=1;i<=n;i++) cha[i] += cha[i-1]; int ans = -1, num = 1; for(int i=1;i<=n;i++) {
if( cha[i]>=num ) ans = i,num = cha[i]; } for(int i=0;i<=n+1;i++) cha[i] = 0; printf("%d\n",ans); }}

转载地址:https://issue-is-vegetable.blog.csdn.net/article/details/114434479 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:P2178 [NOI2015] 品酒大会(并查集+后缀数组)
下一篇:卡拉巴什的字符串(后缀自动机SAM)

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月28日 01时07分12秒