因为不会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 串(后缀数组+二分答案+主席树)的更多相关文章

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

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

  2. bzoj3277 串 (后缀数组+二分答案+ST表)

    常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...

  3. BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案

    Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...

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

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

  5. BZOJ5343: [Ctsc2018]混合果汁 二分答案+主席树

    分析: 整体二分或二分答案+主席树,反正没有要求强制在线,两个都可以做... 贪心还是比较显然的,那么就是找前K大的和...和CQOI的任务查询系统很像 附上代码: #include <cstd ...

  6. BZOJ_5343_[Ctsc2018]混合果汁_二分答案+主席树

    BZOJ_5343_[Ctsc2018]混合果汁_二分答案+主席树 题意:给出每个果汁的价格p,美味度d,最多能放的体积l.定义果汁混合后的美味度为果汁的美味度的最小值. m次询问,要求花费不大于g, ...

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

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

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

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

  9. 2019杭电多校第四场hdu6621 K-th Closest Distance(二分答案+主席树)

    K-th Closest Distance 题目传送门 解题思路 二分答案+主席树 先建主席树,然后二分答案mid,在l和r的区间内查询[p-mid, p+mid]的范围内的数的个数,如果大于k则说明 ...

随机推荐

  1. Linux shell(3)

    shell的运算操作: let整数运算 expr整数运算 bc浮点运算 字符串运算 let命令: let命令让BASH shell 执行算数值的操作,使用let,可以比较两个值或执行加减乘除等这样的算 ...

  2. 经典笔试题:用C写一个函数测试当前机器大小端模式

    “用C语言写一个函数测试当前机器的大小端模式”是一个经典的笔试题,如下使用两种方式进行解答: 1. 用union来测试机器的大小端 #include <stdio.h> union tes ...

  3. 通过Task异步加快对数组的运算

    一.介绍 Task是.NetFramework3.0出现的,线程是基于线程池,然后提供了丰富的API. 先用AverageAssign方法把一组数据平均分成n组,再通过遍历n组数据,循环开Task多线 ...

  4. 关于Netty的学习前总结

    摘要 前段时间一直在学习netty因为工作忙的原因没有写一个学习的总结,今天抽个空先把总结写了吧.事先声明,本文不会详细的介绍每一个部分不过每个部分都会附上讲解详细的url.本文只是为了解释通Nett ...

  5. 零基础学python之入门和列表数据(附详细的代码解释和执行结果截图)

    Python学习笔记 1 快速入门 下载安装好Python之后,在开始找到 双击打开一个窗口,这是一个shell界面编辑窗口,点击左上角的file——new file新建一个窗口,这里可以输入完整的代 ...

  6. Redis源码阅读(五)集群-故障迁移(上)

    Redis源码阅读(五)集群-故障迁移(上) 故障迁移是集群非常重要的功能:直白的说就是在集群中部分节点失效时,能将失效节点负责的键值对迁移到其他节点上,从而保证整个集群系统在部分节点失效后没有丢失数 ...

  7. Dede织梦验证码不显示,织梦后台登陆验证码不显示解决方法

    关于"织梦验证码不显示"的解决方法 "织梦验证码无法显示出来"的问题分析? 1.之前显示正常,但是换了服务器后就不能够正常显示:(这种通常是网站程序经过迁移后所 ...

  8. 基于Python的信用评分卡模型分析(一)

    信用风险计量体系包括主体评级模型和债项评级两部分.主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡.B卡.C卡和F卡:债项评级模型通常按照主体的融资用途,分为 ...

  9. Hyperledger Fabric(v1.2.0)代码分析1——channel创建

    Hyperledger Fabric(v1.2.0)代码分析1--channel创建 0. e2e_cli Hyperledger Fabric提供了一个e2e的例子,该例中创建了一个基础的区块链网络 ...

  10. 配置Ubuntu16.04虚拟机 (用途:CTF_pwn)

    因为学习需要16.xx的虚拟机,所以把之前18.04的Ubuntu卸掉重装了一遍Ubuntu16.04, 考虑到我有备份和重装系统的爱好,故记之,以备后用. 目录: //最后更新时间:190122·1 ...