/* 返回顶部 */

Luogu P3975 [TJOI2015]弦论

gate

求第\(k\)小子串。
可以用后缀自动机求解。

\(t=0\),不同位置的相同子串算作一个。设\(cnt[i]\)表示\(i\)\(endpos\)集合大小,即结束位置为\(i\)的子串个数。因为相同子串只算一次,所以直接设所有点的\(cnt=1\)即可。
\(t=1\),不同位置的相同字串算作多个。这时正常维护\(cnt[i] = cnt[i]+\sum cnt[j](i=fa[j])\)即可。
\(f[i]\)表示经过\(i\)的子串数量。
\(f[i] = cnt[i] + \sum f[j](i=fa[j])\)
按拓扑序从后往前更新一遍\(parent\)树。

找第\(k\)小子串:
从根节点开始遍历。设\(x\)表示下一个字符,枚举\(a-z\)
\(k>f[ch[x]]\),则说明要找的子串不在\(ch[x]\)的子树上,\(k\)减去该子树大小,\(k-f[ch[x]]\)
否则,说明在\(ch[x]\)的子树上,输出字符\(x\),走到节点\(ch[x]\)\(k\)减去这个节点自身的大小,\(k-cnt[ch[x]]\)
直到\(k\le 0\)时,说明当前子串已经找到,退出。

注意:

  • 最后可能会有\(k<0\),因为一个节点上可能有多个子串。
  • 复制节点时,不要忘记设置新节点的\(cnt=0\)

\(code\)

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;

const int maxn = 5e5+10;

int t,k,b[maxn<<1],rk[maxn<<1],f[maxn<<1];
char s[maxn];

struct SuffixAutomaton {
	struct node {
		int ch[26],fa,len,cnt;
		void clean() {
			memset(ch,0,sizeof(ch));
			fa = len = cnt = 0;
		}
	} S[maxn<<1];
	int root,last,siz;

	void init() {
		for(int i = 1; i <= siz; i++)
			S[i].clean();
		root = last = siz = 1;
	}

	void insert(int c) {
		int p = last, now = ++siz;
		S[now].cnt = 1;
		S[now].len = S[p].len+1;
		for(; p && !S[p].ch[c]; p = S[p].fa)
			S[p].ch[c] = now;
		if(!p) S[now].fa = root;
		else {
			int q = S[p].ch[c];
			if(S[q].len == S[p].len+1)
				S[now].fa = q;
			else {
				int q_new = ++siz;
				S[q_new] = S[q];
				S[q_new].cnt = 0;
				S[q_new].len = S[p].len+1;
				S[now].fa = S[q].fa = q_new;
				for(; p && S[p].ch[c] == q; p = S[p].fa)
					S[p].ch[c] = q_new;
			}
		}
		last = now;
	}

	void calc() {
		for(int i = 1; i <= siz; i++)
			b[S[i].len]++;
		for(int i = 1; i <= siz; i++)
			b[i] += b[i-1];
		for(int i = 1; i <= siz; i++)
			rk[b[S[i].len]--] = i;
		for(int i = siz; i; i--)
			if(t) S[S[rk[i]].fa].cnt += S[rk[i]].cnt;
			else S[rk[i]].cnt = 1;
		S[root].cnt = 0;
		for(int i = siz; i; i--) {
			f[rk[i]] = S[rk[i]].cnt;
			for(int j = 0; j < 26; j++)
				f[rk[i]] += f[S[rk[i]].ch[j]];
		}
	}

	void solve() {
		if(k > f[root]) {
			printf("-1");
			return;
		}
		int now = root;
		while(k > 0) {
			for(int i = 0; i < 26; i++) {
				if(!S[now].ch[i]) continue;
				if(k > f[S[now].ch[i]])
					k -= f[S[now].ch[i]];
				else {
					now = S[now].ch[i];
					k -= S[now].cnt;
					printf("%c",i+'a');
					break;
				}
			}
		}
	}

} SAM;

int main() {
	scanf("%s",s+1);
	scanf("%d%d",&t,&k);
	int n = strlen(s+1);
	SAM.init();
	for(int i = 1; i <= n; i++)
		SAM.insert(s[i]-'a');
	SAM.calc();
	SAM.solve();
	return 0;
}
posted @ 2020-07-16 16:37  Mogeko  阅读(92)  评论(0编辑  收藏  举报