[TJOI2015]弦论(后缀数组or后缀自动机)
解法一:后缀数组
听说后缀数组解第k小本质不同的子串是一个经典问题。
把后缀排好序后第i个串的本质不同的串的贡献就是\(n-sa[i]+1-LCP(i,i-1)\)然后我们累加这个贡献,看到哪一个串的时候,这个贡献的和大于等于k,然后答案就在这个串里了,然后枚举就行了。
那么第k小子串该怎么办?
我们考虑二分答案,我们按字典序大小二分一个子串(具体就是二分第k小的本质不同子串,因为这个串可以\(O(n)\)求),然后看看比这个串小的串有多少个?然后改变上下界就行了。
那么我们如何求出比一个串小的串有多少个?
设我们我们二分的子串是后缀数组排名为x的后缀的前缀,长度为len。贡献就是\(\sum_{i=1}^{x-1}n-sa[i]+1+\sum_{i=x}^{n}min(LCP(x,i),len)\)
然后这个题就解决了。
代码很丑
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=501000;
int c[N],x[N],sa[N],y[N],height[N],rk[N],n,m,t,k,tmp,ans;
char s[N];
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
void get_sa(){
for(int i=1;i<=n;i++)c[x[i]=s[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=1;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;
for(int i=1;i<=n;i++)swap(x[i],y[i]);
x[sa[1]]=1;num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(n==num)return;
m=num;
}
}
void get_height(){
int k=0;
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1;i<=n;i++){
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
int judge(int x){
int num=0;
tmp=0;
for(int i=1;i<=n;i++){
if(tmp+n-sa[i]+1-height[i]>=x){
int len=0;
for(int j=sa[i];j-sa[i]+1-height[i]<=x-tmp;j++)len++;
int mn=height[i+1];
num+=len;
for(int j=i+1;j<=n;j++){
mn=min(height[j],mn);
if(height[j]<len){
for(int k=j;k<=n;k++){
mn=min(height[k],mn);
num+=mn;
}
return num;
}
num+=len;
}
}
num+=n-sa[i]+1;
tmp=tmp+n-sa[i]+1-height[i];
}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
m=122;
get_sa();get_height();
t=read();k=read();
if(n*(n+1)/2<k){
printf("-1");
return 0;
}
if(t==0){
for(int i=1;i<=n;i++){
if(tmp+n-sa[i]+1-height[i]>=k){
for(int j=sa[i];j-sa[i]+1-height[i]<=k-tmp;j++)printf("%c",s[j]);
return 0;
}
tmp=tmp+n-sa[i]+1-height[i];
}
}
else{
int l=1,r=k;
while(l<=r){
int mid=(l+r)>>1;
if(judge(mid)>=k){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
tmp=0;
for(int i=1;i<=n;i++){
if(tmp+n-sa[i]+1-height[i]>=ans){
for(int j=sa[i];j-sa[i]+1-height[i]<=ans-tmp;j++)printf("%c",s[j]);
return 0;
}
tmp=tmp+n-sa[i]+1-height[i];
}
}
return 0;
}
解法二 后缀自动机
表示后缀自动机根本不会用。555
trans数组看做边的话一个\(DAG\),从这个\(root\)出发的每一条路径对应原串的一个子串这些子串都是本质不同的。我们可以做一个DP求出从一个点出发的所有路径有多少条路径转移方程\(dp[u]=1+\sum dp[v]\)。然后再在图上像类似线段树上二分的方法就可以求出答案了。
那么第二问该怎么办?
我们注意到一个串出现的次数就是后缀树中这个节点的子树内的后缀节点数(就是代表一个串结束的节点数)。所以我们可以仿照第一问的方案,只不过DP的方程改为了\(dp[u]=size[u]+\sum dp[v]\)(这里的\(size[u]\)代表后缀树中\(u\)的子树的后缀节点数)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1001000;
int tot=1,u=1,len[N],size[N],fa[N],trans[N][27],n,t,k,f[N],c[N],A[N];;
bool vis[N];
char s[N];
void ins(int c){
int x=++tot;size[x]=1;
len[x]=len[u]+1;
for(;u&&trans[u][c]==0;u=fa[u])trans[u][c]=x;
if(u==0)fa[x]=1;
else{
int v=trans[u][c];
if(len[u]+1==len[v])fa[x]=v;
else{
int w=++tot;
len[w]=len[u]+1;
memcpy(trans[w],trans[v],sizeof(trans[w]));fa[w]=fa[v];
fa[v]=fa[x]=w;
for(;u&&trans[u][c]==v;u=fa[u])trans[u][c]=w;
}
}
u=x;
}
void work(int x,int k){
if(k<=size[x]) return;
k-=size[x];
for(int i=1;i<=26;i++){
int R=trans[x][i]; if(!R) continue;
if(k>f[R]) {k-=f[R];continue;}
putchar(i+'a'-1);work(R,k);return;
}
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)ins(s[i]-'a'+1);
t=read();k=read();
if(n*(n+1)/2<k){printf("-1");return 0;}
for(int i=1;i<=tot;i++)c[len[i]]++;
for(int i=1;i<=tot;i++)c[i]+=c[i-1];
for(int i=1;i<=tot;i++)A[c[len[i]]--]=i;
for(int i=tot;i>=1;i--)size[fa[A[i]]]+=size[A[i]];
for(int i=1;i<=tot;i++)t==0?(f[i]=size[i]=1):(f[i]=size[i]);
size[1]=f[1]=0;
for(int i=tot;i>=1;i--)
for(int j=1;j<=26;j++)
if(trans[A[i]][j])f[A[i]]+=f[trans[A[i]][j]];
work(1,k);
return 0;
}
解法三:后缀树
也是类似线段树二分的思想跟SAM差不多,不过不是在图里二分了,在树上二分。
[TJOI2015]弦论(后缀数组or后缀自动机)的更多相关文章
- (持续更新)虚树,KD-Tree,长链剖分,后缀数组,后缀自动机
真的就是讲课两天,吸收一个月呢! \(1.\)虚树 \(2.\)KD-Tree \(3.\)长链剖分 \(4.\)后缀数组 后缀数组 \(5.\)后缀自动机 后缀自动机
- hdu4436-str2int(后缀数组 or 后缀自动机)
题意:给你一堆字符串,仅包含数字'0'到'9'. 例如 101 123 有一个字符串集合S包含输入的N个字符串,和他们的全部字串. 操作字符串很无聊,你决定把它们转化成数字. 你可以把一个字符串转换成 ...
- 字符串数据结构模板/题单(后缀数组,后缀自动机,LCP,后缀平衡树,回文自动机)
模板 后缀数组 #include<bits/stdc++.h> #define R register int using namespace std; const int N=1e6+9; ...
- poj 2774 最长公共子--弦hash或后缀数组或后缀自己主动机
http://poj.org/problem?id=2774 我想看看这里的后缀数组:http://blog.csdn.net/u011026968/article/details/22801015 ...
- poj2774 Long Long Message(后缀数组or后缀自动机)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud Long Long Message Time Limit: 4000MS Me ...
- 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组
涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...
- bzoj 3172 后缀数组|AC自动机
后缀数组或者AC自动机都可以,模板题. /************************************************************** Problem: 3172 Us ...
- SPOJ694 DISUBSTR --- 后缀数组 / 后缀自动机
SPOJ694 DISUBSTR 题目描述: Given a string, we need to find the total number of its distinct substrings. ...
- POJ3080 POJ3450Corporate Identity(广义后缀自动机||后缀数组||KMP)
Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...
随机推荐
- Linux网络配置、文件及命令
Linux的网络配置是曾一直是我学习Linux的埋骨之地,投入了大量的精力和心神但是自己的虚拟机就是联不了网.原来一个大意,我一躺就是一年半.在这里简单的谈谈我对网络的微微认识. VMware的联网模 ...
- springboot中文文档
http://blog.geekidentity.com/spring/spring_boot_translation/ https://blog.csdn.net/zfrong/article/de ...
- 05 ADO.net
ADO.NET就是一组类库 操作数据库用的.
- 开放个人电脑端口[Windows]
先打开控制面板
- POJ 1185 炮兵阵地 (状压dp)(棋盘dp)
这题和poj 3254很像,但是更复杂了一些 都属于棋盘里放东西,然后又各种各样的限制,然后求方案或者最大值 (1)上一道题距离要大于1,这道题是大于2.所以判断的时候变成 !(x & (x ...
- 2015 Multi-University Training Contest 5 hdu 5352 MZL's City
MZL's City Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- 洛谷—— P3388 【模板】割点(割顶)
https://www.luogu.org/problem/show?pid=3388 题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n, ...
- php抽奖、随机广告算法
我们先完毕后台PHP的流程,PHP的主要工作是负责配置奖项及相应的中奖概率,当前端页面点击翻动某个方块时会想后台PHP发送ajax请求,那么后台PHP依据配置的概率,通过概率算法给出中奖结果,同一时候 ...
- 浅谈SaaS应用开发的难度
近期做SaaS应用的非常多,这样的模式是未来的一种趋势,这样的模式的最大优点就是云计算的优点--节约资源.网上有非常多人觉得SaaS非常easy,就是一个多用户租赁模式.这样的认识也不能说不正确.由于 ...
- keras安装及使用
安装全称参考https://keras-cn.readthedocs.io/en/latest/for_beginners/keras_linux/ 环境中已配置cuda8.0.cudnn5.0,ub ...