【链接】h在这里写链接


【题意】


    给你一个长度为n的子串;

    让你求出第k小的子串是什么;

    输出答案的类型分两种;

    第一种,重复的算两次,第二种,重复的算一次。

     

    你的程序要能分别处理这两种情况。

    n最大5*10^5

【题解】


    感觉是道裸的后缀数组了。

    先求出后缀数组;

    然后按照后缀的大小升序枚举每一个后缀就好。

 

    对于重复算作一个的情况,则用每一个后缀的长度减去Height[i];

        这个被减过的值就是以i开头的子串,且不和之前出现过的重复的子串的个数了。

        ->也即算出来字典序为k的子串是多少。

    对于重复算作多个的情况。感觉反而不好写。。

        这样做.

        先把排名前i的后缀的所有的子串的个数算出来,->做一个前缀和

        便于后续的操作。

        然后,对于最后的子串的每一位,

        枚举它应该为多少

        ->一个类似数位DP的做法。

         

        假如最后的子串的第一位是'c'

        那么

        形如

        axxxxxxx

        bxxxxxxx

        的后缀,都能够把它删掉了。

        也即把所有小于它的子串全都减掉。

        直到

        'j'xxxxx的子串个数>=k为止。

        这样就能确定第一位是j.

        然后再枚举第二位是什么也从'a'..'z'

            直到能够确定每一位是什么为止。

 

        我们可以用二分+Sa来快速确定第i位是j的后缀有多少个。

        再用一开始处理好的前缀和。

        就能知道第i位是j的后缀,包含的子串个数。



        (一开始二分的范围是1..n)

        (这个范围表示的是后缀排名为1..n)

        (之后我们会逐渐缩小这个二分的范围)

        (左端点会逐渐变大..)

        (一旦确定第一位是某个字符之后,二分排名的范围就都会变小了..

            因为【第一位是某个字符的后缀】是【集中在一片区域里面】的,同理第二位也确定

            了之后,这个范围【又会变小】了。)

        (所以二分的时候,这片区域的后缀的前i-1位全都是相同的)

             

        从小到大枚举,逐渐确定每一位字符。

        具体实验看程序吧

【错的次数】


0

【反思】


类似数位DP确定每一位是什么的思路。

【代码】

#include<bits/stdc++.h>
#define ll long long
using namespace std; const int N = 5e5;
const int MAX_CHAR = 255;//每个数字的最大值。
char s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存
int Sa[N + 10], T1[N + 10], T2[N + 10], C[N + 10];
int Height[N + 10], Rank[N + 10]; void build_Sa(int n, int m) {
int i, *x = T1, *y = T2;
for (i = 0; i<m; i++) C[i] = 0;
for (i = 0; i<n; i++) C[x[i] = s[i]]++;
for (i = 1; i<m; i++) C[i] += C[i - 1];
for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
for (i = n - k; i<n; i++) y[p++] = i;
for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k;
for (i = 0; i<m; i++) C[i] = 0;
for (i = 0; i<n; i++) C[x[y[i]]]++;
for (i = 1; i<m; i++) C[i] += C[i - 1];
for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[Sa[0]] = 0;
for (i = 1; i<n; i++)
x[Sa[i]] = y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + k] == y[Sa[i] + k] ? p - 1 : p++;
if (p >= n) break;
m = p;
}
} void getHeight(int n)
{
int i, j, k = 0;
for (i = 1; i <= n; i++) Rank[Sa[i]] = i;
for (i = 0; i<n; i++) {
if (k) k--;
j = Sa[Rank[i] - 1];
while (s[i + k] == s[j + k]) k++;
Height[Rank[i]] = k;
}
} ll k, sum[N + 10];
int T, n; void print(int x, int y)
{
for (int i = x; i <= y; i++)
putchar(s[i]);
} int main() {
//freopen("F:\\rush.txt", "r", stdin);
scanf("%s", s);
scanf("%d%lld", &T, &k);
n = strlen(s);
s[n] = 0;
build_Sa(n + 1, MAX_CHAR);//注意调用n+1
getHeight(n);
if (T == 0)
{
ll ans = 0;
for (int i = 1; i <= n; i++)
{
int t = n - Sa[i] - Height[i];
if (k <= t)
return print(Sa[i], (int)k + Sa[i] + Height[i] - 1), 0;
k -= t;
}
}
else
{
sum[0] = 0;
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + n - Sa[i];
//sum[i]表示排名第i的后缀的子串个数
if (sum[n] < k) return puts("-1"), 0;
int L = 1, R = n, pl = 1;
for (int i = 1; i <= n; i++)
{
int l2 = L;
for (int j = 'a'; j <= 'z'; j++)
{
int l = l2, r = R, temp = l2 - 1;
while (l <= r)
{
int mid = (l + r) >> 1;
if (s[Sa[mid] + i - 1] > j)//如果第i为大于j,就变小一点
{
r = mid - 1;
}
else//小于等于->其实就是等于了
{
temp = mid;
l = mid + 1;
}
}
if (temp == l2 - 1)//没找到第i为j的情况的。直接continue;
{
continue;
}
//找到了多少个后缀以xxxj为前缀 ->temp-l2+1;
//然后算出来变成xxxj...会添加多少个子串
//只要把后缀的前缀为xxxj的后缀的子串个数累加起来,然后减去之前重复计数过
//的就好
ll have = sum[temp] - sum[l2 - 1] - 1LL * (i - 1)*(temp - l2 + 1);
//前面i-1位那个子串已经计数过了,不重复计数
if (k <= have)
{
//如果第i位是j的后缀的子串数目大于等于k了
//则可以肯定第i为肯定是j
if (temp - l2 + 1 >= k)//如果j是最后一个字符了,直接输出就好
return print(Sa[l2], Sa[l2] + i - 1), 0;
L = l2, R = temp;//否则可以缩小二分的范围,因为第i位已经固定是j了,只有某一段的后缀的第i位是j
k -= temp - l2 + 1;//xxxxj一共有temp-l2+1个
//要减去
break;
}
l2 = temp + 1, k -= have;//把第i位为j的后缀去掉,之后就不会管它了。
//所以直接跳到temp+1就好,然后因为第i位不是j,是大于j的,所以把xxxj...
//它所有的子串都去掉就好
}
}
}
puts("-1");
return 0;
}

【BZOJ 3998】弦论的更多相关文章

  1. bzoj 3998 弦论 —— 后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3998 关于相同子串算一个还是算多个,其实就是看一种状态的 right 集合是否加上 Pare ...

  2. BZOJ - 3998 弦论 (后缀自动机)

    #include<cstdio> #include<cstring> #include<queue> using namespace std; typedef lo ...

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

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

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

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

  5. BZOJ 3998 [TJOI 2015] 弦论 解题报告

    这是一道后缀自动机经典题目. 对于 $t=0$ 的情况:每个节点都代表一个子串,所以我们给每个节点的 $Size$ 都记为 $1$, 对于 $t=1$ 的情况:我们只给 $last$ 节点的 $Siz ...

  6. ●BZOJ 3998 [TJOI2015]弦论

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3998题解: 后缀自动机. 当T=0时, 由于在后缀自动机上沿着trans转移,每个串都是互不 ...

  7. BZOJ 3998: [TJOI2015]弦论 后缀自动机 后缀自动机求第k小子串

    http://www.lydsy.com/JudgeOnline/problem.php?id=3998 后缀自动机应用的一个模板?需要对len进行一个排序之后再统计每个出现的数量,维护的是以该字符串 ...

  8. bzoj 3998 [TJOI2015]弦论——后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3998 相同子串算多个的话,先求好 right ,然后求一个 sm 表示走到这个点之后有几种走 ...

  9. BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3998 题意概述:对于一个给定长度为N的字符串,求它的第K小子串是什么,T为0则表示不同位置 ...

  10. bzoj 3998: [TJOI2015]弦论【SA+二分||SAM】

    SA的话t==0直接预处理出每个后缀的不同串贡献二分即可,然后t==1就按字典序枚举后缀,然后跳右端点计算和当前后缀的前缀相同的子串个数,直到第k个 不过bzoj上会T #include<ios ...

随机推荐

  1. android 图片特效处理之怀旧效果

    图片特效处理系列将介绍图片的像素点的特效处理,这些物资注重的是原理.也就是说只要你知道这些算法不管是C++,VB,C#,Java都可以做出相同的特效.下面将介绍图片怀旧效果的算法.算法如下: 上面公式 ...

  2. php数组合并有哪三种方法

    php数组合并有哪三种方法 一.总结 一句话总结:array_merge():array_merge_recursive():‘+'号 $a = array('color'=>'red',5,6 ...

  3. php实现简单算法3

    php实现简单算法3 这篇文章主要介绍了PHP经典算法集锦,整理了各种常见的算法,包括排序.查找.遍历.运算等各种常见算法原理与实现技巧,需要的朋友可以参考下 1.首先来画个菱形玩玩,很多人学C时在书 ...

  4. Android Studio 解决unspecified on project app resolves to an APK archive which is not supported

    出现该问题unspecified on project app resolves to an APK archive which is not supported as a compilation d ...

  5. 当Java代码遇上抽象、重载加重写,一切都不美好了

    当Java代码遇上抽象.重载加重写.一切都不美好了 前几天调程序遇上个奇怪的bug.一直没找到问题,今天最终发现问题所在了,不说了先上代码(下面代码是演示样例代码,经測试,Java不存在这问题,安卓存 ...

  6. WINDOWS 安装 M2Crypto for Python2.7

    WINDOWS 安装 M2Crypto for Python2.7运行环境 WIN8.1 + Python2.7 + VS2008(Microsoft Visual C++ 9.0) VS2008 可 ...

  7. 华为畅玩5 (CUN-AL00) 刷入第三方twrp Recovery 及 root

    华为畅玩5 (CUN-AL00) 刷入第三方twrp Recovery  及 root 下载地址    http://pan.baidu.com/s/1hsn6VzA 1. 在官网申请解锁码    申 ...

  8. sqlserver存储过程实现多表分页

    if @PageIndex = 1 begin if @strWhere != ’’ set @strSQL = ’select top ’ + str(@PageSize) +’ ’+@strGet ...

  9. 43.c++指针类型转换

    数据类型转换(static_cast) //数据类型转换 printf("%d\n", static_cast<int>(10.2)); 指针类型转换(reinterp ...

  10. 71.sscanf数据挖掘

    数据挖掘 sscanf(str, "%d %s %s %d %d %s %s %s", &ph[i].id, ph[i].name, ph[i].sex, &ph[ ...