洛谷P3975 [TJOI2015]弦论 (后缀自动机sam求第k小子串)

题目描述

为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?

输入格式

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

第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。

输出格式

输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。

学习了后缀自动机 也终于A了这道感觉很麻烦的题= = 写个博客庆祝一下

求第k小串的套路就像是在主席树上面查询第k小数,首先用后缀自动机处理出每个节点后续的子串的个数,然后对于每个节点枚举26条边转移即可 一个节点所表示的串的出现次数即为endpos集合的size,若对于t = 1相同子串不同位置算不同子串(除复制节点外,其他节点初始|endpos|大小设置为1) 需要在后缀树上面拓扑dp t = 0的话 每个节点只表示它的最长串 所以每个节点的值为1 

 

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#define Sheryang main
typedef long long ll;
#define HEAP(...) priority_queue<__VA_ARGS__ >
#define heap(...) priority_queue<__VA_ARGS__,vector<__VA_ARGS__ >,greater<__VA_ARGS__ > >
template<class T> inline T min(T &x,const T &y){return x>y?y:x;}
template<class T> inline T max(T &x,const T &y){return x<y?y:x;}
//#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
const int maxn = 1e6+7;
const int mod = 1e9+7;
/** keep hungry and keep calm! **/

int nxt[maxn],head[maxn],to[maxn],cnt; 
void add(int u,int v){
	nxt[++cnt] = head[u];
	head[u] = cnt;
	to[cnt] = v;
}

struct Sam{
	int trans[maxn][26],fail[maxn];
	int maxlen[maxn],minlen[maxn];
	int sz,last,vis[maxn];
	ll sum[maxn],num[maxn];
	Sam(){
		sz = last = 1;
	}
	void ins(int id){
		int cur = ++sz,p = last;
		num[cur] = 1;
		maxlen[cur] = maxlen[last] + 1;
		while(p && trans[p][id] == 0){
			trans[p][id] = cur;
			p = fail[p];
		}
		if(p == 0){
			fail[cur] = 1;
		}else{
			int q = trans[p][id];
			if(maxlen[q] == maxlen[p] + 1){
				fail[cur] = q;
			}else{
				int clone = ++sz;
				memcpy(trans[clone],trans[q],sizeof trans[q]);
				fail[clone] = fail[q];
				fail[cur] = fail[q] = clone;
				maxlen[clone] = maxlen[p] + 1;
				while(trans[p][id] == q){
					trans[p][id] = clone;
					p = fail[p];
				}
			}
		}
		last = cur;
	}
	void init(){
		for(int i=1;i<=sz;i++) head[i] = -1;
		for(int i=2;i<=sz;i++){
			add(fail[i],i);
		}
	}
	void dfs(int u){
		for(int i=head[u];~i;i = nxt[i]){
			dfs(to[i]);
			num[u] += num[to[i]];
		}
	}
	void calc(int u){
		sum[u] = num[u];
		vis[u] = 1;
		for(int i=0;i<26;i++){
			int v = trans[u][i];
			if(v != 0){
				if(vis[v] == 0)calc(v);
				sum[u] += sum[v];
			}
		}
	}
	void Fuck(int u,int k){
		int rt = 1;
		while(k){
			for(int i=0;i<26;i++){
				int v = trans[rt][i];
				if(v != 0){
					if(k <= sum[v]){
						putchar('a' + i);
						k -= num[v];
						rt = v;
						break;
					}else{
						k -= sum[v];
					}
				}
			}
		}
	}
	void Solve(){
		int op = read , k = read;
		init();
		if(op == 1){
			dfs(1);
		}else{
			for(int i=1;i<=sz;i++) num[i] = 1;
		}
		num[1] = 0; 
		calc(1);
		if(k > sum[1]){
			printf("-1");
		}else{
			Fuck(1,k);
		}
		puts("");
	}
}Sam;

char s[maxn];
int Sheryang(){
	scanf("%s",s);
	for(int i=0;s[i];i++){
		Sam.ins(s[i]-'a');
	}
	Sam.Solve();
	return 0; 
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值