后缀自动机求字典序第k小的串——p3975
又领悟到了一点新的东西,后缀自动机其实可以分为两个数据结构,一个是后缀树,还有一个是自动机
后缀树用来划分endpos集合,并且维护后缀之间的关系,此时每个结点代表的是一些后缀相同且长度连续的子串
自动机用来处理边的转移,或者用来解决串的匹配问题,此时每个结点代表的只是一个串,这个串等于从root开始到这结点经过的路径,由于路径可能有很多条,所以对应到后缀树上,就是有一段连续的串啦
字典序第k小的串刚好可以用SAM的性质解决
/*
题目要求考虑两种情况:
首先来考虑算重复子串的情况
处理后缀树:先求出每个状态endpos的大小(即这个状态代表的所有串出现的次数)size[i](endpos的大小即该状态的后缀子树的大小,所有的前缀结点贡献为1,克隆结点大小为0)
处理自动机:自底向上求出从每个状态出发能形成的子串个数 sum[i]+=sum{sum[j]} ,初始有sum[i]=size[i](即从这种状态开始有多少不同的子串,当然这种状态本身的方案数也算)
最后以root为起点,在自动机上进行搜索即可,具体搜索方式类似于多叉树(DAG)查询
然后来考虑不算重复子串的情况
处理后缀树:求endpos的大小
处理自动机:自底向上求每个状态出发能形成的子串个数sum[i]=sum{sum[j]} 初始有sum[i]=1,即这个状态的每个串认为只出现一次
再换一种思路:其实只要维护处每个状态作为起点的不同路径数即可
搜索过程不变
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 500005
long long T,K;
char s[maxn];
struct SAM{
int last,cnt;
int nxt[maxn<<][];
int len[maxn<<];
int link[maxn<<];
int size[maxn<<];
long long sum[maxn<<];//sum[i]表示从状态i开始的子串总数
SAM(){
last=cnt=;
}
void add(int c){
int p=last,np=last=++cnt;
len[np]=len[p]+;
size[np]=;
for(;p&&!nxt[p][c];p=link[p])
nxt[p][c]=np;
if(!p){link[np]=;return;} int q=nxt[p][c];
if(len[q]==len[p]+){link[np]=q;return;} int clone=++cnt;
link[clone]=link[q];
len[clone]=len[p]+;
memcpy(nxt[clone],nxt[q],sizeof nxt[q]);
link[q]=link[np]=clone;
for(;p&&nxt[p][c]==q;p=link[p])
nxt[p][c]=clone;
}
int id[maxn<<],t[maxn<<];
void sort(){
for(int i=;i<=cnt;i++)t[len[i]]++;
for(int i=;i<=cnt;i++)t[i]+=t[i-];
for(int i=;i<=cnt;i++)id[t[len[i]]--]=i;
}
void work(){//求子串的总数
//处理后缀树上的endpos
for(int i=cnt;i>=;i--)
size[link[id[i]]]+=size[id[i]];
for(int i=;i<=cnt;i++)
if(T==)sum[i]=size[i]=;//要求去重,每个状态代表的串只统计一次
else sum[i]=size[i];
size[]=sum[]=;
//自底向上处理自动机
for(int i=cnt;i>=;i--)
for(int j=;j<;j++)
if(nxt[id[i]][j])
sum[id[i]]+=sum[nxt[id[i]][j]];
}
}p; void print(long long now,long long k){
if(k<=p.size[now])return;//如果这个结点代表的串也不能走完,返回
k-=p.size[now];
for(int i=;i<;i++){
int tmp=p.nxt[now][i];
if(!tmp)continue;
if(k>p.sum[tmp]){
k-=p.sum[tmp];
continue;
} putchar(i+'a');//沿着这个状态往下搜
print(tmp,k);
return;
}
} int main(){
scanf("%s%lld%lld",s,&T,&K);
int len=strlen(s);
for(int i=;i<len;i++)
p.add(s[i]-'a'); p.sort(); p.work(); if(p.sum[]<K)printf("-1");
else print(,K);
puts(""); }
后缀自动机求字典序第k小的串——p3975的更多相关文章
- hdu 5008 查找字典序第k小的子串
Boring String Problem Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Ot ...
- Leetcode 440.字典序第k小的数字
字典序第k小的数字 给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字. 注意:1 ≤ k ≤ n ≤ 109. 示例 : 输入: n: 13 k: 2 输出: 10 解释: 字典序的排 ...
- BZOJ 3998: [TJOI2015]弦论 后缀自动机 后缀自动机求第k小子串
http://www.lydsy.com/JudgeOnline/problem.php?id=3998 后缀自动机应用的一个模板?需要对len进行一个排序之后再统计每个出现的数量,维护的是以该字符串 ...
- Alice's Classified Message HDU - 5558 后缀自动机求某个后缀出现的最早位置
题意: 给定一个长度不超过 10W 的只包含小写字母的字符串,从下标 0 到 n−1.从下标 0 开始操作, 每次对于下标 pos查找下标 pos 开始的子串中最长的在其他地方出现过的长度,其他出现的 ...
- str2int HDU - 4436 后缀自动机求子串信息
题意: 给出 n 个串,求出这 n 个串所有子串代表的数字的和. 题解; 首先可以把这些串构建后缀自动机(sam.last=1就好了), 因为后缀自动机上从 root走到的任意节点都是一个子串,所有可 ...
- SPOJ COT Count on a tree(树上主席树 + LCA 求点第k小)题解
题意:n个点的树,每个点有权值,问你u~v路径第k小的点的权值是? 思路: 树上主席树就是每个点建一棵权值线段树,具体看JQ博客,LCA用倍增logn求出,具体原理看这里 树上主席树我每个点的存的是点 ...
- poj2886(线段树求序列第k小)
题目链接:https://vjudge.net/problem/POJ-2886 题意:n个人围成一个圈,每个人有姓名s和权值val两个属性,第一轮序号为k的人退出,并根据其val指定下一个人,val ...
- poj2182(线段树求序列第k小)
题目链接:https://vjudge.net/problem/POJ-2182 题意:有n头牛,从1..n编号,乱序排成一列,给出第2..n个牛其前面有多少比它编号小的个数,记为a[i],求该序列的 ...
- 洛谷P4248 [AHOI2013]差异(后缀自动机求lcp之和)
题目见此 题解:首先所有后缀都在最后一个np节点,然后他们都是从1号点出发沿一些字符边到达这个点的,所以下文称1号点为根节点,我们思考一下什么时候会产生lcp,显然是当他们从根节点开始一直跳相同节点的 ...
随机推荐
- 【Luogu】【关卡2-15】动态规划的背包问题(2017年10月)【还差一道题】
任务说明:这是最基础的动态规划.不过如果是第一次接触会有些难以理解.加油闯过这个坎. 01背包二维数组优化成滚动数组的时候有坑有坑有坑!!!必须要downto,downto,downto 情景和代码见 ...
- leetcode-163周赛-1261-在污染的二叉树中查找元素
题目描述: 方法一: class FindElements: def __init__(self, root: TreeNode): self.d = set() def f(r, x): if r: ...
- 区别|Pandas-qcut( )与cut( )的区别
https://blog.csdn.net/starter_____/article/details/79327997
- ecshop前台英文后台中文的设置方法
ecshop前台英文后台中文的设置方法 这里有两种方法: 第一种方法: 打开 admin/includes/init.php 文件 找到$_CFG = load_config(); 在它下面增加一行代 ...
- 【Linux】windows下编写的脚本文件,放到Linux中无法识别格式
注意:我启动的时候遇到脚本错误 » sh startup.sh -m standalone tanghuang@bogon : command not found : command not foun ...
- h5移动端局部放大效果
首先添加zoom.js (function (root, factory) { if (typeof exports === 'object' && typeof module === ...
- PHP反序列化漏洞研究
序列化 序列化说通俗点就是把一个对象变成可以传输的字符串 php serialize()函数 用于序列化对象或数组,并返回一个字符串.序列化对象后,可以很方便的将它传递给其他需要它的地方,且其类型和结 ...
- noip2002 普及组 过河卒
题目描述 棋盘上A点有一个过河卒,需要走到目标B点.卒行走的规则:可以向下.或者向右.同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点.因此称之为"马拦过 ...
- 20165239 2018——2019Exp8 Web基础
Exp8 Web基础 基础问题回答 (1)什么是表单 •表单在网页中主要负责数据采集功能. •一个表单有三个基本组成部分: ◦表单标签,这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务 ...
- 机器学习基石笔记:Homework #3 LinReg&LogReg相关习题
原文地址:http://www.jianshu.com/p/311141f2047d 问题描述 程序实现 13-15 # coding: utf-8 import numpy as np import ...