[BZOJ]3998 [TJOI2015] 弦论 后缀自动机

3998: [TJOI2015]弦论

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 3292   Solved: 1133
[ Submit][ Status][ Discuss]

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^5


T<2

K<=10^9

Source

[ Submit][ Status][ Discuss]


HOME Back

  以前wjj给我讲过但那是暑假的事了... 现在几乎都忘光了... 不过重拾起来还是非常快? 虽然一开始看毛子论文的各种专业术语乱飚一脸懵逼, 不过努力回想wjj讲过的内容在纸上推推还是推出来了... 明白了以后看了下clj神犇的论文发现这玩意性质还非常多? 感觉比今早上学的回文自动机要高级多了... 看来还是得多学习一个... 争取深入理解后缀自动机, 把性质都记录下来推一推多做点题.

  这道题很简单, 两种情况分类讨论逆拓扑序倒推预处理, 然后询问第k大可以说是...26分?

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1000005;
char ss[maxn];
int n, k, T, cnt = 1, last = 1;
int ws[maxn], sa[maxn], c[maxn][26], dep[maxn], val[maxn], sum[maxn], par[maxn];
struct sam{
	inline void build(int x) {
		int p = last, np = last = ++ cnt;
		dep[np] = dep[p] + 1; val[np] = 1;
		while (p && !c[p][x]) c[p][x] = np, p = par[p];
		if (!p) par[np] = 1;
		else {
			int who = c[p][x];
			if (dep[who] == dep[p] + 1) par[np] = who;
			else {
				int anwho = ++ cnt;
				dep[anwho] = dep[p] + 1;
				memcpy(c[anwho], c[who], sizeof(c[who]));
				par[anwho] = par[who];  
				par[np] = par[who] = anwho;  
				while (c[p][x] == who) c[p][x] = anwho, p = par[p];  
			}
		}
	}
	inline void init() {
		for (int i = 1; i <= cnt; ++ i) ws[dep[i]] ++;  
		for (int i = 1; i <= n; ++ i) ws[i] += ws[i - 1];
		for (int i = cnt; i; -- i) sa[ws[dep[i]]--] = i;
		for (int i = cnt; i; -- i) {
			int t = sa[i];
			if (T & 1) val[par[t]] += val[t];
			else val[t] = 1;
		}
		val[1] = 0;
		for (int i = cnt; i; -- i) {
			int t = sa[i]; sum[t] = val[t];
			for (int ch = 0; ch < 26; ++ ch)  
				sum[t] += sum[c[t][ch]];
		}
	}
	void calc(int x,int k) {  
		while(true) {
			if (k <= val[x]) return;
			k -= val[x];
			for (int i = 0; i < 26; ++ i) {  
				int t = c[x][i];  
				if (t) {  
					if (k <= sum[t]) {  
						putchar(i + 'a');  
						x = t;  
						break;
					}
					k -= sum[t];
				}
			}
		}
    }
}sam;  
int main() {
	scanf("%s", ss + 1); 
	n = strlen(ss + 1);
	scanf("%d%d", &T, &k);
	for (int i = 1; i <= n; ++ i) sam.build(ss[i] - 'a');
	sam.init();
	if (k > sum[1]) puts("-1");
	else sam.calc(1,k);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值