洛谷P4248 差异
题意:求所有后缀两两之间的最长公共前缀的长度之和。
解:这道题让我发现了一个奇妙的性质:所有后缀两两最长公共前缀长度之和 和 所有前缀两两最长公共后缀之和的值是相等的,但是每一组公共前/后缀是不同的。
因为这道题要反建后缀自动机,我正建然后过了......
两个串的最长公共后缀就是在fail树上的lca。
所以反建后缀自动机,所有后缀就是主链。然后求这些两两的lca计算每个点作为lca被统计了多少次,树上DFS一遍就好了。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 5 typedef long long LL; 6 const int N = 1000010; 7 8 struct Edge { 9 int nex, v; 10 }edge[N]; int top; 11 12 int tr[N][26], len[N], fail[N], cnt[N], tot = 1, last = 1; 13 int e[N], siz[N]; 14 LL ans; 15 char s[N]; 16 17 inline void add(int x, int y) { 18 top++; 19 edge[top].v = y; 20 edge[top].nex = e[x]; 21 e[x] = top; 22 return; 23 } 24 25 inline void insert(char c) { 26 int f = c - 'a'; 27 int p = last, np = ++tot; 28 last = np; 29 len[np] = len[p] + 1; 30 cnt[np] = 1; 31 while(p && !tr[p][f]) { 32 tr[p][f] = np; 33 p = fail[p]; 34 } 35 if(!p) { 36 fail[np] = 1; 37 } 38 else { 39 int Q = tr[p][f]; 40 if(len[Q] == len[p] + 1) { 41 fail[np] = Q; 42 } 43 else { 44 int nQ = ++tot; 45 len[nQ] = len[p] + 1; 46 fail[nQ] = fail[Q]; 47 fail[Q] = fail[np] = nQ; 48 memcpy(tr[nQ], tr[Q], sizeof(tr[Q])); 49 while(tr[p][f] == Q) { 50 tr[p][f] = nQ; 51 p = fail[p]; 52 } 53 } 54 } 55 return; 56 } 57 58 inline void prework() { 59 for(int i = 2; i <= tot; i++) { 60 add(fail[i], i); 61 //printf("add : %d %d \n", fail[i], i); 62 } 63 return; 64 } 65 66 void DFS(int x) { 67 siz[x] = cnt[x]; 68 for(int i = e[x]; i; i = edge[i].nex) { 69 int y = edge[i].v; 70 DFS(y); 71 if(x > 1) { 72 ans += 1ll * siz[x] * siz[y] * len[x]; 73 } 74 siz[x] += siz[y]; 75 } 76 //printf("x = %d siz = %d \n", x, siz[x]); 77 return; 78 } 79 80 int main() { 81 LL sum = 0; 82 scanf("%s", s); 83 int n = strlen(s); 84 for(int i = 0; i < n; i++) { 85 insert(s[i]); 86 sum += 1ll * (n - 1) * (i + 1); 87 } 88 prework(); 89 DFS(1); 90 printf("%lld\n", sum - 2 * ans); 91 //printf("%lld - %lld \n", sum, 2 * ans); 92 return 0; 93 }