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

题目

对于一个给定长度为N的字符串,求它的第K小子串是什么。

输入格式

第一行是一个仅由小写英文字母构成的字符串S

第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。

输出格式

输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1

输入样例

aabc

0 3

输出样例

aab

提示

N<=5*10^5

T<2

K<=10^9

题解

肝了一个中午的论文还是想了好久这种裸题。。

由后缀自动机从根节点走每个节点都是一种子串的性质,我们能很快解决T=0的问题

T=0:

令每个节点值都为1【除了根】,按拓扑逆序向儿子统计

T=1:

每个点不再只是代表一个串了,其代表的串的个数等于其Right集合的大小

那么在parent树上统计每个点子树中的结束节点有多少个

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k; k = ed[k].nxt)
using namespace std;
const int maxn = 1000005,maxm = 100005,INF = 1000000000;
int pre[maxn],step[maxn],ch[maxn][26],last,cnt,n,sz[maxn];
int a[maxn],b[maxn],sum[maxn];
char s[maxn];
void ins(int x){
    int p = last,np = ++cnt;
    last = np; step[np] = step[p] + 1;
    while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
    if (!p) pre[np] = 1;
    else {
        int q = ch[p][x];
        if (step[q] == step[p] + 1) pre[np] = q;
        else {
            int nq = ++cnt; step[nq] = step[p] + 1;
            for (int i = 0; i < 26; i++) ch[nq][i] = ch[q][i];
            pre[nq] = pre[q]; pre[np] = pre[q] = nq;
            while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
        }
    }
    sz[np] = 1;
}
void dfs(int u,int k){
    if (k <= sz[u]) return;
    k -= sz[u];
    for (int i = 0; i < 26; i++){
        if (int t = ch[u][i]){
            if (k <= sum[t]){
                putchar(‘a‘+ i);
                dfs(t,k);
                return;
            }
            k -= sum[t];
        }
    }
}
void solve(){
    int T,k;
    scanf("%d%d",&T,&k);
    REP(i,cnt) b[step[i]]++;
    REP(i,cnt) b[i] += b[i - 1];
    REP(i,cnt) a[b[step[i]]--] = i;
    for (int i = cnt; i; i--){
        int u = a[i];
        if (T == 1) sz[pre[u]] += sz[u];
        else sz[u] = 1;
    }
    sz[1] = 0;
    for (int i = cnt; i; i--){
        int u = a[i]; sum[u] = sz[u];
        for (int j = 0; j < 26; j++)
            sum[u] += sum[ch[u][j]];
    }
    REP(i,cnt) printf("%d ",sum[i]); puts("");
    if (k > sum[1]) {puts("-1"); return;}
    dfs(1,k);
}
int main(){
    scanf("%s",s + 1);
    n = strlen(s + 1); last = cnt = 1;
    REP(i,n) ins(s[i] - ‘a‘);
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8304146.html

时间: 2024-10-06 03:26:41

BZOJ3998 [TJOI2015]弦论 【后缀自动机】的相关文章

[bzoj3998][TJOI2015]弦论——后缀自动机

题目大意: 给定一个字符串,求它的第k小子串. 思路: 后缀自动机的模板题. 考虑将后缀自动机建出来之后,求出每一个状态在原串中出现了多少次,然后统计以每个节点往后拓展的字符串总共有多少种(按照拓扑序累加即可),最后直接在DAG上做类似二分的就好了. /*======================================= * Author : ylsoi * Time : 2019.2.9 * Problem : bzoj3998 * E-mail : [email protecte

BZOJ3998 TJOI2015 弦论 后缀自动机

题意:求一个字符串的第K小字串,T=0表示不同位置相同的子串算作一个,T=1算作多个 题意: 建出SAM来跑第K子串,由于一个点所代表的子串在原串出现次数为其子树叶子结点的数量,因而有: T==1,每个点的|right|=1 T==2,每个点的|right|=子树叶子结点数 BFS跑出所有子串出现的次数即|right|,DFS统计每个节点的出现次数和,具体细节看代码. 然后处理询问即可. #include <cstdio> #include <cstring> #include &

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

[BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. Output 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 Sample Input aabc 0 3 Sample Output aab HINT N<=5*10

【bzoj3998】[TJOI2015]弦论 后缀自动机+dp

题目描述 对于一个给定长度为N的字符串,求它的第K小子串是什么. 输入 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. 输出 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 样例输入 aabc 0 3 样例输出 aab 题解 后缀自动机+dp 先对原串建立后缀自动机,然后在其上面跑dp统计每个节点开始的串的个数. 设f[i]表示与位置i有相同前缀的串

bzoj 3998: [TJOI2015]弦论(后缀自动机)

题目链接:bzoj 3998: [TJOI2015]弦论 题意: 对于一个给定长度为N的字符串,求它的第K小子串是什么. 题解: 后缀自动机O(n)*26解决. 对于op=0,num[i]=1,对于op=1,num[i]=cnt[i]. 因为cnt[i](即right集)表示以i节点结尾的后缀出现的次数. 1 #include<cstdio> 2 #include<cstring> 3 #define F(i,a,b) for(int i=a;i<=b;++i) 4 #def

【BZOJ-3998】弦论 后缀自动机

3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2018  Solved: 662[Submit][Status][Discuss] Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. Output 输出仅

[TJOI2015]弦论 - 后缀自动机

下了狠心开始做SAM的题目了-- (中间因为傻逼26分写错被卡,进来的时候记得把自己的 cnt 减掉) // TJOI2015 XIAN LUN #include <bits/stdc++.h> using namespace std; const int Maxn = 2000005; struct Suffix_Automata { int maxlen[Maxn], trans[Maxn][26], link[Maxn], Size, Last; int t[Maxn], a[Maxn]

【BZOJ3998】【TJOI2015】弦论 后缀自动机

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/45369569"); } 题解: 首先我们可以建一个后缀自动机. 然后每条路径走到每个点都是一个串,它们是有字典序的. 我们只需要统计出往每个点走之后都有多少串就好了. fi=(∑fson)+numi 对于不计重复的情况下,numi=

Bzoj3998 [TJOI2015]弦论

Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2388  Solved: 798 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. Output 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 Sample In