zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    给定一个长度为 n 的字符串 S,令 Ti 表示它从第 i 个字符开始的后缀。求:

    [sum_{1le i < j le n}((len(Ti) -lcp(Ti, Tj)+(len(Tj)-lcp(Ti, Tj)) ]

    其中 lcp 是最长公共前缀。

    input
    一个长度为 n 的字符串S。2 <= n <= 500000,S由小写英文字母组成。
    output
    一个整数,表示所求值。

    sample input
    cacao
    sample output
    54

    @solution@

    那个式子我们可以分两部分求解:len 和 lcp。

    len 部分:每一个后缀的 len 都会统计 n-1 次。所以它对答案的贡献为 ((1+2+...+n)*(n-1))。把等差数列的求和公式代进去:(frac{(n-1)*n*(n+1)}{2})

    lcp 部分:我们把原串翻转,则原串中的后缀对应新串中的前缀,我们要求解原串中的最长公共前缀就是新串中的最长公共后缀。
    一个结点的 fa 所表示的结点一定是这个结点的后缀。所以我们最长公共后缀所表示的结点一定是该结点的某个祖先。
    那么两个结点的 lca 就能表示它们的最长公共后缀。因此我们作一个简单的树形 dp 统计有多少对点以根为 lca 即可。

    实际上,后缀自动机在翻转的串上建出来的 parent 树,就是原串中的后缀树。

    所以后缀树完完全全没什么用啊喂。

    @accepted code@

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 500000;
    vector<int>G[2*MAXN + 5];
    struct sam{
    	sam *ch[26], *fa; int mx;
    	int tag;
    }pl[2*MAXN + 5], *root, *tcnt, *lst;
    void init() {
    	root = tcnt = lst = &pl[0];
    }
    sam *newnode(int x) {
    	tcnt++; tcnt->tag = x;
    	return tcnt;
    }
    void clone(sam *x, sam *y) {
    	for(int i=0;i<26;i++)
    		x->ch[i] = y->ch[i];
    	x->fa = y->fa;
    }
    void sam_extend(int x) {
    	sam *cur = newnode(1), *p = lst;
    	cur->mx = lst->mx + 1; lst = 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( p->mx + 1 == q->mx )
    			cur->fa = q;
    		else {
    			sam *nq = newnode(0);
    			nq->mx = p->mx + 1;
    			clone(nq, q);
    			q->fa = cur->fa = nq;
    			while( p && p->ch[x] == q )
    				p->ch[x] = nq, p = p->fa;
    		}
    	}
    }
    int siz[2*MAXN + 5];
    char s[MAXN + 5]; ll ans;
    void dfs(int rt) {
    	siz[rt] = pl[rt].tag;
    	for(int i=0;i<G[rt].size();i++) {
    		int to = G[rt][i]; dfs(to);
    		ans -= 2LL*siz[rt]*siz[to]*pl[rt].mx;
    		siz[rt] += siz[to];
    	}
    }
    int main() {
    	init(); scanf("%s", s);
    	int lens = strlen(s);
    	ans = 1LL*(lens-1)*lens*(lens+1)/2;
    	for(int i=lens-1;i>=0;i--)
    		sam_extend(s[i] - 'a');
    	for(int i=1;i<=tcnt-pl;i++) {
    		G[pl[i].fa-pl].push_back(i);
    	}	
    	dfs(0);
    	printf("%lld
    ", ans);
    }
    

    @details@

    所以真的想问问大家,后缀树既然可以通过后缀自动机构造出来,时间复杂度空间复杂度也不会更优秀(因为你不可能超过线性复杂度嘛)。

    那么后缀树到底用处在哪里?

  • 相关阅读:
    定时器应用-最终版
    定时器应用-点击按钮,div向右移动
    通过js读取元素的样式
    延时调用
    定时器应用-切换图片的练习
    BOM对象属性定时器的调用
    BOM浏览器对象模型
    键盘移动
    Python-字符串方法
    Python实现注册和登录
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10256459.html
Copyright © 2011-2022 走看看