zoukankan      html  css  js  c++  java
  • 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]])
    直到(kle 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;
    }
    
  • 相关阅读:
    初遇黑客
    第四周学习总结
    第三周学习总结
    关于base64编码的原理及如何在python中实现
    在python中如何将十进制小数转换成二进制
    《信息安全专业导论》第二周学习总结
    计算机科学概论速读问题
    刘谨铭的自我介绍
    师生关系
    20201318快速浏览教材提问
  • 原文地址:https://www.cnblogs.com/mogeko/p/13323402.html
Copyright © 2011-2022 走看看