zoukankan      html  css  js  c++  java
  • @spoj


    @description@

    给定一个由小写字母构成的字符串。
    多次询问。询问它相异子串中字典序第 K 小的子串。

    input
    第一行给出字符串 S(长度小于等于 90000)。
    第二行给出 Q,表示询问个数。Q <= 500。
    接下来 Q 行,每行一个 K,表示询问字典序第 K 小的子串。

    output
    输出 Q 行,第 i 行输出第 i 个询问的答案。

    sample input
    aaa
    2
    2
    3
    sample output
    aa
    aaa

    @solution@

    子串必须要不相同。那就只好用后缀自动机咯。

    我们知道,后缀自动机的主体部分 DAWG 是一个有向无环图。
    我们还知道,后缀自动机中每一个结点表示若干子串。

    因此,我们可以求出从某一结点出发能够表示多少子串,记为 size。
    假如我们从初始结点寻找字典序最小的子串,肯定是沿着字典序最小的边走。经过上面的处理后,我们可以判断经过这个结点一定能得到字典序最小,第二小,...,第 size 小的子串。
    我们要寻找的第 K 小,就可以判断一下是否经过这个结点,如果是就走这个结点,否则寻找下一条相应的边。

    @accepted code@

    #include<cstdio>
    #include<cstring>
    const int MAXN = 90000;
    typedef long long ll;
    struct node{
    	node *ch[26], *fa; int mx;
    	node *nxt; ll siz;
    }pl[2*MAXN + 5], *bin[MAXN + 5], *tcnt, *root, *lst;
    void init() {
    	tcnt = root = lst = &pl[0];
    }
    node *newnode() {
    	tcnt++;
    	return tcnt;
    }
    void add_bin(node *x) {
    	x->nxt = bin[x->mx];
    	bin[x->mx] = x;
    }
    void clone(node *x, node *y) {
    	for(int i=0;i<26;i++)
    		x->ch[i] = y->ch[i];
    	x->fa = y->fa;
    }
    void extend(int x) {
    	node *cur = newnode(), *p = lst;
    	cur->mx = lst->mx + 1; lst = cur;
    	add_bin(cur);
    	while( p && !p->ch[x] )
    		p->ch[x] = cur, p = p->fa;
    	if( !p )
    		cur->fa = root;
    	else {
    		node *q = p->ch[x];
    		if( p->mx + 1 == q->mx )
    			cur->fa = q;
    		else {
    			node *nq = newnode();
    			clone(nq, q); nq->mx = p->mx + 1;
    			add_bin(nq);
    			cur->fa = q->fa = nq;
    			while( p && p->ch[x] == q )
    				p->ch[x] = nq, p = p->fa;
    		}
    	}
    }
    char s[MAXN + 5];
    int main() {
    	init(); scanf("%s", s);
    	int lens = strlen(s);
    	for(int i=0;i<lens;i++)
    		extend(s[i] - 'a');
    	for(int i=lens;i>=1;i--)
    		while( bin[i] ) {
    			bin[i]->siz = 1;
    			for(int j=0;j<26;j++)
    				if( bin[i]->ch[j] ) bin[i]->siz += bin[i]->ch[j]->siz;
    			bin[i] = bin[i]->nxt;
    		}
    	int Q; scanf("%d", &Q);
    	for(int i=1;i<=Q;i++) {
    		int K; scanf("%d", &K);
    		node *nw = root;
    		while( K ) {
    			for(int i=0;i<26;i++)
    				if( nw->ch[i] ) {
    					if( K > nw->ch[i]->siz )
    						K -= nw->ch[i]->siz;
    					else {
    						printf("%c", i + 'a');
    						nw = nw->ch[i]; K--;
    						break;
    					}
    				}
    		}
    		puts("");
    	}
    }
    

    @details@

    事实上,如果你处理出每个子串的出现次数(这也是后缀自动机常做的事情),也可以根据这个思路求解可以重复的第 K 小子串问题。

  • 相关阅读:
    SoundTouch
    80211
    netsh wlan
    jest--cmd
    必须精通nuxt了,不可变,to thi
    bili实际的ssr
    vscode 调试vuetify
    【Java】 第四章 异常处理 Notes learn Ma
    Windows 下的符号链接 小示例
    Java 第一二章 配置基础 与 java 数据类型
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10254769.html
Copyright © 2011-2022 走看看