BZOJ3277 串(后缀数组+二分答案+主席树)
因为不会SAM,考虑SA。将所有串连起来并加分隔符,每次考虑计算以某个位置开始的子串有多少个合法。
对此首先二分答案,找到名次数组上的一个区间,那么只需要统计有多少个所给串在该区间内出现就可以了。这是一个主席树的经典问题,对每个数找到上次出现位置扔进去即可。这样就做到O(nlog2n)了。
可以进一步优化到O(nlogn),不是很会。
以及我又不会写SA了,没救。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 200010
int n,m,tot,pos[N],cnt[N],end[N],sa[N],sa2[N],rk[N<<],tmp[N<<],h[N],lg2[N],f[N][],root[N],pre[N],p[N],cnt2=;
long long ans[N];
struct data{int l,r,x;
}tree[N<<];
char s[N],c[N];
void make(int n)
{
int m=;
for (int i=;i<=n;i++) cnt[rk[i]=s[i]]++,m=max(m,(int)s[i]);
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[i]]--]=i;
for (int k=;k<=n;k<<=)
{
int p=;
for (int i=n-k+;i<=n;i++) sa2[++p]=i;
for (int i=;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
memset(cnt,,sizeof(cnt));
for (int i=;i<=n;i++) cnt[rk[i]]++;
for (int i=;i<=m;i++) cnt[i]+=cnt[i-];
for (int i=n;i>=;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];
memcpy(tmp,rk,sizeof(rk));
p=rk[sa[]]=;
for (int i=;i<=n;i++)
{
if (tmp[sa[i]]!=tmp[sa[i-]]||tmp[sa[i]+k]!=tmp[sa[i-]+k]) p++;
rk[sa[i]]=p;
}
m=p;if (m>=n) break;
}
for (int i=;i<=n;i++)
{
h[i]=max(h[i-]-,);
while (s[i+h[i]]==s[sa[rk[i]-]+h[i]]) h[i]++;
}
for (int i=;i<=n;i++) f[i][]=h[sa[i]];
lg2[]=;
for (int i=;i<=n;i++)
{
lg2[i]=lg2[i-];
if ((<<lg2[i])<=i) lg2[i]++;
}
for (int j=;j<=;j++)
for (int i=;i<=n;i++)
f[i][j]=min(f[i][j-],f[min(i+(<<j-),n)][j-]);
}
int query(int x,int y)
{
if (x>y) swap(x,y);
x++;if (x>y) return N;
return min(f[x][lg2[y-x+]],f[y-(<<lg2[y-x+])+][lg2[y-x+]]);
}
void ins(int &k,int l,int r,int x)
{
tree[++cnt2]=tree[k],k=cnt2;tree[k].x++;
if (l==r) return;
int mid=l+r>>;
if (x<=mid) ins(tree[k].l,l,mid,x);
else ins(tree[k].r,mid+,r,x);
}
bool Query(int x,int y,int l,int r,int p,int m)
{
if (m<=) return ;
if (!y) return ;
if (l==r) return m<=tree[y].x-tree[x].x;
int mid=l+r>>;
if (p<=mid) return Query(tree[x].l,tree[y].l,l,mid,p,m);
else return Query(tree[x].r,tree[y].r,mid+,r,p,m-tree[tree[y].l].x+tree[tree[x].l].x);
}
void build()
{
for (int i=;i<=tot;i++) pre[i]=p[pos[sa[i]]],p[pos[sa[i]]]=i;
for (int i=;i<=tot;i++)
{
root[i]=root[i-];
ins(root[i],,tot,pre[i]);
}
}
bool check(int x,int k,int m)
{
int p,q,l=,r=x;
while (l<=r)
{
int mid=l+r>>;
if (query(mid,x)>=k) p=mid,r=mid-;
else l=mid+;
}
l=x,r=tot;
while (l<=r)
{
int mid=l+r>>;
if (query(mid,x)>=k) q=mid,l=mid+;
else r=mid-;
}
return Query(root[p-],root[q],,tot,p-,m);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj3277.in","r",stdin);
freopen("bzoj3277.out","w",stdout);
const char LL[]="%I64d ";
#else
const char LL[]="%lld ";
#endif
n=read(),m=read();
for (int i=;i<=n;i++)
{
scanf("%s",c+);int m=strlen(c+);
for (int j=;j<=m;j++) s[++tot]=c[j],pos[tot]=i;
s[++tot]='$',pos[tot]=i;end[i]=tot;
}
make(tot);
build();
for (int i=;i<=tot;i++)
if (s[i]!='$')
{
int l=,r=end[pos[i]]-i,x=;
while (l<=r)
{
int mid=l+r>>;
if (check(rk[i],mid,m)) x=mid,l=mid+;
else r=mid-;
}
ans[pos[i]]+=x;
}
for (int i=;i<=n;i++) printf(LL,ans[i]);
return ;
}
BZOJ3277 串(后缀数组+二分答案+主席树)的更多相关文章
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串(后缀数组 + 二分答案 + 主席树 + ST表 or 后缀数组 + 暴力)
题意 一个长为 \(n\) 的字符串 \(s\),和 \(m\) 个询问.每次询问有 \(4\) 个参数分别为 \(a,b,c,d\). 要你告诉它 \(s[a...b]\) 中的所有子串 和 \(s ...
- bzoj3277 串 (后缀数组+二分答案+ST表)
常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案
Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...
- BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案
BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案 Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l 读入单 ...
- BZOJ5343: [Ctsc2018]混合果汁 二分答案+主席树
分析: 整体二分或二分答案+主席树,反正没有要求强制在线,两个都可以做... 贪心还是比较显然的,那么就是找前K大的和...和CQOI的任务查询系统很像 附上代码: #include <cstd ...
- BZOJ_5343_[Ctsc2018]混合果汁_二分答案+主席树
BZOJ_5343_[Ctsc2018]混合果汁_二分答案+主席树 题意:给出每个果汁的价格p,美味度d,最多能放的体积l.定义果汁混合后的美味度为果汁的美味度的最小值. m次询问,要求花费不大于g, ...
- Poj 1743 Musical Theme(后缀数组+二分答案)
Musical Theme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 28435 Accepted: 9604 Descri ...
- Poj 3261 Milk Patterns(后缀数组+二分答案)
Milk Patterns Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk g ...
- 2019杭电多校第四场hdu6621 K-th Closest Distance(二分答案+主席树)
K-th Closest Distance 题目传送门 解题思路 二分答案+主席树 先建主席树,然后二分答案mid,在l和r的区间内查询[p-mid, p+mid]的范围内的数的个数,如果大于k则说明 ...
随机推荐
- 转载-30分钟搞定后台登录界面(103个后台PSD源文件、素材网站)
原文:30分钟搞定后台登录界面(103个后台PSD源文件.素材网站) 目录 一.界面预览 二.PSD源文件预览 三.工具分享 四.资源说明 五.素材下载网站 六.下载 去年八月时要做一个OA系统为 ...
- 【Unity Shader】(六) ------ 复杂的光照(上)
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Sha ...
- centos7上的postgresql10安装和配置
安装数据库 安装参考官方文档:https://www.postgresql.org/download/linux/redhat/ 1.Install the repository RPM: yum i ...
- Gitlab 403 forbidden 并发引起IP被封
问题 在工作中自搭建的Gitlab.但今天打开页面的时候显示的是空白页面,上面还有一次文本Forbidden. 原因 Gitlab使用rack_attack做了并发访问的限制. 解决方案 将Gitla ...
- Unity摄像机围绕物体旋转两种实现方式
第一种,使用Transform 函数 RotateAround. 代码如下: public Transform target;//获取旋转目标 private void camerarotate() ...
- yocto-sumo源码解析(十): ProcessServer.idle_commands
这一节开始介绍ProcessServer.idle_commands,前面我们知道ProcessServer.main就是不停调用idle_commands()以获取可用的套接字描述符或者是文件描述符 ...
- Docker部署Golang
1. 安装docker 2. mkdir myDocker 3. cd myDocker && touch Dockerfile 4. Dockerfile写入 # 将golang ...
- 多线程分段下载研究的python实现(一)
我一直对下载文件比较感兴趣.现在我下载文件大部分是用迅雷,但迅雷也有一些不如意的地方,内存占用大,一些不必要的功能太多,不可定制.尤其是最后一点.现在有些下载对useragent,cookie,aut ...
- e2fsck命令详解
原文链接:https://ipcmen.com/e2fsck Linux e2fsck命令用于检查使用 Linux ext2 档案系统的 partition 是否正常工作. 语法 e2fsck [-p ...
- 7行Python代码的人脸识别
随着去年alphago 的震撼表现,AI 再次成为科技公司的宠儿.AI涉及的领域众多,图像识别中的人脸识别是其中一个有趣的分支.百度的BFR,Face++的开放平台,汉王,讯飞等等都提供了人脸识别的A ...