洛谷P4248 差別

題意:求全部後綴兩兩之間的最長公共前綴的長度之和。ide

解:這道題讓我發現了一個奇妙的性質:全部後綴兩兩最長公共前綴長度之和 和 全部前綴兩兩最長公共後綴之和的值是相等的,可是每一組公共前/後綴是不一樣的。spa

由於這道題要反建後綴自動機,我正建而後過了......code

兩個串的最長公共後綴就是在fail樹上的lca。blog

因此反建後綴自動機,全部後綴就是主鏈。而後求這些兩兩的lca計算每一個點做爲lca被統計了多少次,樹上DFS一遍就行了。string

 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 }
AC代碼(僞)
相關文章
相關標籤/搜索