• @spoj



    @description@

    给定一个仅包含小写字母的字符串 S,对于每一个 i 满足 1 <= i <= |S|,求长度为 i 的,在 S 中出现次数最多的串出现了多少次?

    input
    输入一个长度小于等于 250000 的,仅包含小写字母的串。

    output
    输出 |S| 行,第 i 行表示长度为 i 的在 S 中出现次数最多的串的出现次数。

    sample input
    ababa
    sample output
    3
    2
    2
    1
    1

    @solution@

    想想我们在后缀自动机中 end-pos 的含义:每一次出现的结束位置的集合。
    我们要求解它出现了多少次,即要求解 |end-pos|。

    再想想我们构建后缀自动机采用的是增量法。
    每次加入一个字符,就会多出来一个以前从未出现过的一个新结束位置。

    然后想想我们 fa 的含义,一个结点的 end-pos 必然是 fa 的 end-pos 的子集。
    所以假如多出来一个新的结束位置,那么它会影响它所有的祖先。

    最后,一个结点的最长子串长度必然大于它 fa 的最长子串,因此我们可以按照最长子串长度进行桶排序,再从后往前扫一边,更新父亲的值,就可以求解出每一个结点的出现次数。

    假如某一个结点出现次数为 k,那么它的最长子串的所有后缀出现次数也一定大于等于 k,所以我们可以直接用 k 去更新最长子串长度的值,再从后往前用后一个去更新前一个。

    @accepted code@

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN = 250000;
    struct sam{
    	sam *ch[26], *fa; int mx;
    	sam *nxt; int siz;
    }pl[2*MAXN + 5], *bin[MAXN + 5], *tcnt, *root, *lst;
    void init() {
    	tcnt = root = &pl[0];
    	for(int i=0;i<26;i++)
    		root->ch[i] = NULL;
    	root->fa = NULL, root->mx = 0;
    }
    void add_bin(sam *x) {
    	x->nxt = bin[x->mx];
    	bin[x->mx] = x;
    }
    sam *newnode() {
    	tcnt++;
    	for(int i=0;i<26;i++)
    		tcnt->ch[i] = NULL;
    	tcnt->fa = NULL, tcnt->mx = 0;
    	return tcnt;
    }
    void sam_extend(int x) {
    	sam *cur = newnode(), *p = lst;
    	cur->mx = lst->mx + 1, cur->siz = 1, lst = cur;
    	add_bin(cur);
    	while( p && !p->ch[x] )
    		p->ch[x] = cur, p = p->fa;
    	if( !p )
    		cur->fa = root;
    	else {
    		sam *q = p->ch[x];
    		if( q->mx == p->mx + 1 )
    			cur->fa = q;
    		else {
    			sam *cne = newnode();
    			(*cne) = (*q), cne->mx = p->mx + 1, cne->siz = 0;
    			add_bin(cne);
    			q->fa = cur->fa = cne;
    			while( p && p->ch[x] == q )
    				p->ch[x] = cne, p = p->fa;
    		}
    	}
    }
    char s[MAXN + 5]; int ans[MAXN + 5];
    int main() {
    	init(); lst = root;
    	scanf("%s", s); int lens = strlen(s);
    	for(int i=0;i<lens;i++)
    		sam_extend(s[i] - 'a');
    	for(int i=lens;i>=1;i--) {
    		while( bin[i] ) {
    			bin[i]->fa->siz += bin[i]->siz;
    			ans[i] = max(ans[i], bin[i]->siz);
    			bin[i] = bin[i]->nxt;
    		}
    	}
    	for(int i=1;i<=lens;i++)
    		printf("%d
    ", ans[i]);
    }
    

    @details@

    我们的 end-pos 大小并不等于子树大小,而是等于子树内非复制的(即每一次用增量法加入的)点的个数。

  • 相关阅读:
    【WEBI专题】解决在交叉表中计算占比、偏差、率的问题
    前导零的问题(增加/删除)
    在SAP BW中使用ABAP
    如何组织维度
    BEx Query Designer中的变量及其增强
    《魔幻手机》经典台词摘录
    今天发现了VISTA系统的一个BUG
    Online Tools And Sofrware Directory Entry
    [转载]博客园通过Word 2007发布随笔
    为你的程序增加记忆功能
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10251943.html
Copyright © 2020-2023  润新知