@bzoj - [email protected] [Ahoi2013]差異
阿新 • • 發佈:2019-01-11
目錄
@[email protected]
給定一個長度為 n 的字串 S,令 Ti 表示它從第 i 個字元開始的字尾。求:
\[\sum_{1\le 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
@[email protected]
那個式子我們可以分兩部分求解: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 [email protected]
#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\n", ans); }
@[email protected]
所以真的想問問大家,字尾樹既然可以通過後綴自動機構造出來,時間複雜度空間複雜度也不會更優秀(因為你不可能超過線性複雜度嘛)。
那麼字尾樹到底用處在哪裡?