我们先求出该字符串的\(SA\)和\(Ht\)

然后分类讨论

\(T=0\)时,每次去掉\(Ht\)往后扫就行

\(T=1\)时,我们考虑\(lcp\)对答案的影响

既然用到\(lcp\),那就要用\(ST\)表维护

左端点固定时,随右端点的增大,区间\(\min\)单调不升

我们就可以用二分+限制右端点的方式统计某个后缀的前缀的贡献

循环遍历每个后缀时,由于\(Ht\)部分已经统计过对答案的贡献,就不用再计算了,从\(Ht+1\)的位置继续向后统计即可

#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
using namespace std; const int MAXN=5e5+5; int ln,maxn,p,t;
int SA[MAXN],id[MAXN],rnk[MAXN],bnk[MAXN],Ht[MAXN];
char ch[MAXN];
int ST[19][MAXN],lg[MAXN]; void shel()
{
for(int i=1;i<=ln;++i) ++bnk[rnk[id[i]]];
for(int i=1;i<=maxn;++i) bnk[i]+=bnk[i-1];
for(int i=1;i<=ln;++i) SA[++bnk[rnk[id[i]]-1]]=id[i];
for(int i=0;i<=maxn;++i) bnk[i]=0;
return;
} void GetSA()
{
for(int i=1;i<=ln;++i) rnk[i]=ch[i],id[i]=i,maxn=max(maxn,rnk[i]);
shel();
for(int k=1;k<ln;k<<=1){
for(int i=1;i<=k;++i) id[i]=ln-k+i;
int ct=k;
for(int i=1;i<=ln;++i) if(SA[i]>k) id[++ct]=SA[i]-k;
shel();swap(id,rnk);rnk[SA[1]]=1;
for(int i=2;i<=ln;++i) rnk[SA[i]]=(id[SA[i]]==id[SA[i-1]]&&id[SA[i]+k]==id[SA[i-1]+k])?rnk[SA[i-1]]:rnk[SA[i-1]]+1;
if(rnk[SA[ln]]==ln) return;
maxn=rnk[SA[ln]];
}return;
} void GetHt()
{
int k=0;
for(int i=1;i<=ln;++i){
if(rnk[i]==1) continue;
if(k) --k;
int ct=SA[rnk[i]-1];
while(ct+k<=ln&&i+k<=ln&&ch[i+k]==ch[ct+k]) ++k;
Ht[rnk[i]]=k;
}return;
} int Getmi(int l,int r){int tmp=lg[r-l+1];return min(ST[tmp][l+(1<<tmp)-1],ST[tmp][r]);} void slv1()
{
for(int i=1;i<=ln;++i) lg[i]=i>>lg[i-1]+1?lg[i-1]+1:lg[i-1];
for(int i=1;i<=ln;++i) ST[0][i]=Ht[i];
for(int j=1;j<=lg[ln];++j){
int tmp=1<<j-1;
for(int i=tmp<<1;i<=ln;++i){
ST[j][i]=min(ST[j-1][i],ST[j-1][i-tmp]);
}
}for(int i=1;i<=ln;++i){
int tmp=SA[i]+Ht[i];
int ct=ln;
while(1){
if(tmp>ln) break;
if(ct==i){
if(t>ln-tmp+1){t-=ln-tmp+1;break;}
for(int j=SA[i];j<=tmp+t-1;++j) putchar(ch[j]);
return;
}int l=i,r=ct;
while(l<r){
int mid=l+r+1>>1;
if(Getmi(i+1,mid)>=tmp-SA[i]+1) l=mid;
else r=mid-1;
}ct=r;
if(t>r-i+1) t-=r-i+1;
else{
for(int j=SA[i];j<=tmp;++j) putchar(ch[j]);
return;
}++tmp;
}
}puts("-1");
return;
} void slv2()
{
int ct=1;
while(t>ln-SA[ct]-Ht[ct]+1) t-=ln-SA[ct]-Ht[ct]+1,++ct;
if(ct>ln){puts("-1");return;}
for(int i=SA[ct];i<=SA[ct]+t+Ht[ct]-1;++i) putchar(ch[i]);
puts("");
return;
} int main()
{
scanf("%s",ch+1);ln=strlen(ch+1);
GetSA();GetHt();scanf("%d%d",&p,&t);
p?slv1():slv2();
return 0;
}

[TJOI2015]弦论的更多相关文章

  1. BZOJ 3998: [TJOI2015]弦论 [后缀自动机 DP]

    3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2152  Solved: 716[Submit][Status] ...

  2. Luogu P3975 [TJOI2015]弦论

    题目链接 \(Click\) \(Here\) 题目大意: 重复子串不算的第\(k\)大子串 重复子串计入的第\(k\)大子串 写法:后缀自动机. 和\(OI\) \(Wiki\)上介绍的写法不太一样 ...

  3. 洛谷 P3975 [TJOI2015]弦论 解题报告

    P3975 [TJOI2015]弦论 题目描述 为了提高智商,ZJY开始学习弦论.这一天,她在<String theory>中看到了这样一道问题:对于一个给定的长度为\(n\)的字符串,求 ...

  4. 【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )

    3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2627  Solved: 881 Description 对于一 ...

  5. 【BZOJ3998】[TJOI2015]弦论 后缀自动机

    [BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T ...

  6. BZOJ_3998_[TJOI2015]弦论_后缀自动机

    BZOJ_3998_[TJOI2015]弦论_后缀自动机 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行 ...

  7. bzoj3998: [TJOI2015]弦论(SAM+dfs)

    3998: [TJOI2015]弦论 题目:传送门 题解: SAM的入门题目(很好的复习了SAM并加强Right集合的使用) 其实对于第K小的字符串直接从root开始一通DFS就好,因为son边是直接 ...

  8. luogu P3975 [TJOI2015]弦论 SAM

    luogu P3975 [TJOI2015]弦论 链接 bzoj 思路 建出sam. 子串算多个的,统计preant tree的子树大小,否则就是大小为1 然后再统计sam的节点能走到多少串. 然后就 ...

  9. LGOJ3975 TJOI2015 弦论

    link:TJOI2015 弦论 题目大意: 给定一个字符串,输出在对该字符串所有的非空子串排序后第\(k\)个 另外的一个限制是\(T\):子串本质相同但位置不同算\(1\)或多个 \(|s| \l ...

  10. 题解-TJOI2015 弦论

    TJOI2015 弦论 字符串 \(s\) 和 \(t\) 和 \(k\).如果 \(t=0\),不同位置的相同子串算 \(1\) 个:如果 \(t=1\),不同位置的相同子串算多个.求 \(k\) ...

随机推荐

  1. js 标准二维数组变一维数组的方法

    问题:[[0, 1], [2, 3], [4, 5]] -> [0, 1, 2, 3, 4, 5]? 方法一 利用es5的arr.reduce(callback[, initialValue]) ...

  2. JQuery 限制文本输入只能输入数字(可自定义正则表达式)

    var JVerify = { role: { number: /[0-9\/]/, decimal: /[0-9\.\/]/, code: /[0-9A-Z]/ }, Verify: functio ...

  3. 《Python编程从入门到实践》--- 学习过程笔记(3)列表

    一.用[](方括号)表示列表,用,(逗号)分隔其中的元素. >>> name=['limei', 'hanmeimei', 'xiaoming'] >>> prin ...

  4. [Leetcode]134.加油站

    这一题是贪心不是模拟 是贪心不是模拟 是贪心不是模拟! 如果用模拟的做法会比较慢,也失去了做这一题的趣味了. 模拟的方法很简单,就是每一个加油站都做起点模拟一遍,试一下能不能完成一圈,能完成一圈就保存 ...

  5. 匿名类、包、权限修饰符_DAY10

    1:内部类(理解) (1)把类定义在一个类的内部. (2)特点:   A:内部类可以直接使用外部类的成员,包括私有.   B:外部类要使用内部类成员,必须创建对象使用.    例子: public c ...

  6. Twitter Bootstrap3小结

    今天有空,小结一下Twitter Bootstrap 3的使用.首先不得不说,Bootstrap是迄今(2014)比较好的WEB设计框架(当然,其它的优秀WEB Framework还有:Foundat ...

  7. SwitchHosts—hosts管理工具

    SwitchHosts是一个管理.快速切换Hosts小工具,开源软件,一键切换Hosts配置,非常实用,高效.开发Web过程成,部署有多套环境,网址域名都相同,部署在不同的服务器上,有开发环境.测试环 ...

  8. Chapter 3 Phenomenon——21

    "Nobody will believe that, you know." “你知道吗没有人会相信会是这样的” His voice held an edge of derision ...

  9. 多线程编程(六)-Executor与ThreadPoolExecutor的使用

    使用Executors工厂类创建线程池 1.使用newCachedThreadPool()方法创建无界线程池 newCachedThreadPool()方法创建的是无界线程池,可以进行线程自动回收,此 ...

  10. windows10 设置软件开机启动

    在 C:\Users\your_name\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 在这个目录下,新建你想开机启动的软 ...