常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割

由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串中出现过

那我们就二分一下这个前缀的长度。现在的问题就是怎么判断这个前缀是否在K个串中出现过了。

显然,对于一个后缀s的长度为x的前缀,只要某个后缀t 和s的LCP>=x,就说明x也是t的后缀

我们知道,LCP(x,y)=min{height[rank[y]],height[rank[y]-1],...,height[rank[x]+1]},所以其实t和s的LCP也是单调的,满足条件的是一个排名区间

那我们用二分出来这个区间的左右端点(用st表预处理一下height的区间最小值),然后只要判断这个区间里的后缀是否满足出现在K个串中就可以了

我们只要提前处理出来left[i],表示一个最大的排名,使得sa[left[i]]...sa[i]在不同串中出现次数>=K,然后判断我刚二分出来的那个区间[l,r],是否l<=left[r]就可以了

复杂度$O(nlog^2n)$,据说有点卡常,那稍微优化一下

我们第一次二分前缀的长度的时候,显然现在在做的的最大的长度一定是>=(上一个后缀的最大前缀长度-1)的,所以只要像求height那样推着做就可以了

据说均摊复杂度O(nlogn)

(第一次写后缀数组各种懵...最后还是全都照着大佬的题解抄的...)

 #include<bits/stdc++.h>
#define pa pair<int,int>
#define ll long long
using namespace std;
const int maxn=2e5+; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} int NN,N,M,K;
char ch[maxn];
int suf[maxn],rank[maxn],rank1[maxn],cnt[maxn],tmp[maxn];
int h[maxn],left0[maxn],bel[maxn],st[maxn][],pos[maxn][]; inline void getsuf(){
int i,j,k;M=;
for(i=;i<=N;i++) cnt[ch[i]]=;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) rank[i]=cnt[ch[i]];
for(k=;j!=N;k<<=){
memset(cnt,,sizeof(cnt));
for(i=;i<=N;i++) cnt[rank[i+k>N?:i+k]]++;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) tmp[cnt[rank[i+k>N?:i+k]]--]=i;
memset(cnt,,sizeof(cnt));
for(i=;i<=N;i++) cnt[rank[i]]++;
for(i=;i<=M;i++) cnt[i]+=cnt[i-];
for(i=N;i;i--) suf[cnt[rank[tmp[i]]]--]=tmp[i];
memcpy(rank1,rank,sizeof(rank));
rank[suf[]]=j=;
for(i=;i<=N;i++){
int ipk=suf[i]+k>N?:suf[i]+k,i1pk=suf[i-]+k>N?:suf[i-]+k;
if(rank1[ipk]!=rank1[i1pk]||rank1[suf[i]]!=rank1[suf[i-]]) j++;
rank[suf[i]]=j;
}M=j;
}
for(i=;i<=N;i++) suf[rank[i]]=i;
} inline void geth(){
//h[1]=1;
for(int i=,j=;i<=N;i++){
if(rank[i]==) continue;
if(j) j--;
int x=suf[rank[i]-];
while(i+j<=N&&x+j<=N&&ch[i+j]==ch[x+j]) j++;
h[rank[i]]=j;
}
} inline void getleft0(){
int n=;
memset(cnt,,sizeof(cnt));
for(int i=,j=;i<=N;i++){
if(ch[suf[i]]=='z'+) continue;
if(!cnt[bel[suf[i]]]) n++;
cnt[bel[suf[i]]]++;
if(n>=K){
for(;n-(cnt[bel[suf[j]]]==)>=K;n-=(cnt[bel[suf[j++]]]==)) cnt[bel[suf[j]]]--;
left0[i]=j;
}
}
} inline void getst(){
for(int i=N;i;i--){
st[i][]=h[i];
for(int j=;st[i+(<<(j-))][j-];j++){
st[i][j]=min(st[i][j-],st[i+(<<(j-))][j-]);
}
}
} inline int rmq(int l,int r){
int k=log2(r-l+);
return min(st[l][k],st[r-(<<k)+][k]);
} inline bool check(int p,int x){
int l,r,l0,r0;
if(h[p+]<x) r0=p;
else{
l=p+;r=N;
while(l<=r){
int m=(l+r)>>;
if(rmq(p+,m)>=x)r0=m,l=m+;
else r=m-;
}
}
if(h[p]<x) l0=p;
else{
l=;r=p;
while(l<=r){
// printf("%d %d\n",l,r);
int m=(l+r)>>;
if(rmq(m,p)>=x) l0=m,r=m-;
else l=m+;
}l0--;
}
return left0[r0]>=l0;
} inline void solve(){
for(int i=;i<=NN;i++){
int k=;ll ans=;
for(int j=pos[i][];j<pos[i][];j++){
if(k) k--;
for(;j+k<pos[i][]&&check(rank[j],k+);k++);
ans=ans+k;
}
printf("%lld ",ans);
}printf("\n");
} int main(){
//freopen("3277.in","r",stdin);
// freopen("aa.out","w",stdout);
int i,j,k;
N=NN=rd(),K=rd();
for(i=,j=;i<=N;i++){
pos[i][]=j;
scanf("%s",ch+j);k=strlen(ch+j);
ch[j+k]='z'+;
for(int t=j;t<j+k;t++) bel[t]=i;
j=j+k+;
pos[i][]=j-;
}N=j-;
getsuf();
geth();
getleft0();
getst();
solve();
return ;
}

bzoj3277 串 (后缀数组+二分答案+ST表)的更多相关文章

  1. [BZOJ3277/BZOJ3473] 串 - 后缀数组,二分,双指针,ST表,均摊分析

    [BZOJ3277] 串 Description 现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Solution 首先将所有串连 ...

  2. BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案

    BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案 Description          给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单 ...

  3. Poj 1743 Musical Theme(后缀数组+二分答案)

    Musical Theme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 28435 Accepted: 9604 Descri ...

  4. Poj 3261 Milk Patterns(后缀数组+二分答案)

    Milk Patterns Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk g ...

  5. BZOJ 4556: [Tjoi2016&Heoi2016]字符串(后缀数组 + 二分答案 + 主席树 + ST表 or 后缀数组 + 暴力)

    题意 一个长为 \(n\) 的字符串 \(s\),和 \(m\) 个询问.每次询问有 \(4\) 个参数分别为 \(a,b,c,d\). 要你告诉它 \(s[a...b]\) 中的所有子串 和 \(s ...

  6. BZOJ3277 串(后缀数组+二分答案+主席树)

    因为不会SAM,考虑SA.将所有串连起来并加分隔符,每次考虑计算以某个位置开始的子串有多少个合法. 对此首先二分答案,找到名次数组上的一个区间,那么只需要统计有多少个所给串在该区间内出现就可以了.这是 ...

  7. [HEOI2016] 字符串 - 后缀数组,主席树,ST表,二分

    [HEOI2016] 字符串 Description 给定一个字符串 \(S\), 有 \(m\) 个询问,每个询问给定参数 \((a,b,c,d)\) ,求 \(s[a..b]\) 的子串与 \(s ...

  8. 跳蚤[BZOJ4310](后缀数组+二分答案传判定)

    不知道后缀数组的请退回去! 题面: 题目描述 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究.首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S ...

  9. 【BZOJ2946】[Poi2000]公共串 后缀数组+二分

    [BZOJ2946][Poi2000]公共串 Description        给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单词 l        计 ...

随机推荐

  1. Luogu P2048 [NOI2010]超级钢琴

    这道题题号很清新啊!第一次开NOI的题,因为最近考到了这道题的升级版. 我们先考虑\(O(n^2)\)大暴力,就是枚举前后端点然后开一个前缀和减一下即可. 然后引入正解,我们设一个三元组\(F(s,l ...

  2. 基于uFUN开发板的心率计(二)动态阈值算法获取心率值

    前言 上一篇文章:基于uFUN开发板的心率计(一)DMA方式获取传感器数据,介绍了如何获取PulseSensor心率传感器的电压值,并对硬件电路进行了计算分析.心率计,重要的是要获取到心率值,本篇文章 ...

  3. python语言程序设计?

    1, 别说了,我还是有几分蛋疼的.女朋友..计算机..唉 2, 今天把那几个练习写完吧? 3, 这个注释有啥用最前面的?? 4, 我在学完python后必须学完C和C++并开始离散数学和线代高数等全复 ...

  4. 【JUC源码解析】CompletableFuture

    简介 先说Future, 它用来描述一个异步计算的结果.isDone方法可以用来检查计算是否完成,get方法可以用来获取结果,直到完成前一直阻塞当前线程,cancel方法可以取消任务.而对于结果的获取 ...

  5. REST-framework快速构建API--源码解析

    一.APIView 通过APIView实现API的过程如下: urls.py url(r'^books/$', views.BookView.as_view(),name="books&qu ...

  6. item 11: 比起private undefined function优先使用deleted function

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你为其他开发者提供代码,并且你想阻止他们调用一个特定的函数,你 ...

  7. Bash : 冒泡排序

    冒泡排序是非常基础的排序算法,本文我们看看在 Bash 脚本中如何写冒泡排序.本文的演示环境为 ubuntu 16.04. 冒泡排序的简要描述如下: 通过连续的比较对数组中的元素进行排序 比较两个相邻 ...

  8. Windows 10 中 VMware 要求禁用 Device Guard 问题

    今天在打开虚拟机的时候,突然出现下面这个错误.网上给了很多教程,基本上都是禁用 Device Guard 和关闭 Hyper-v,博主按照其方法操作,依旧出现下面错误.后来经过不懈努力,终于找到解决办 ...

  9. Aop笔记

    参考: https://blog.csdn.net/bombSKLK/article/details/79143145 示例 拦截的 注解的方法 @Around("@annotation(c ...

  10. spring boot之mybatis配置

    配置在application.yml文件中 mybatis-plus: # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Map ...