有个双倍经验2865,我T + RE + MLE......
然而...这个蓝色的背景是怎么回事...
线段树 + SAM。
考虑SAM的每个节点,显然只有cnt == 1的节点有贡献。因为cnt = 1所有只有一个right集合。于是其中比len[fail]短的备选答案就是lenfail +1,其余的备选答案就依次增长。
因为李超树很毒瘤就换个方式,发现斜率只可能是-1,于是维护真实值 + i的值。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 5 const int M = 800010, N = 500010; 6 7 int tr[M][26], len[M], fail[M], cnt[M], bin[M], topo[M], tot, last, right[M]; 8 char str[N]; 9 int small[N * 4], tag[N * 4], n; 10 11 inline void init() { 12 last = tot = 1; 13 return; 14 } 15 16 inline void insert(char c, int id) { 17 int f = c - 'a', p = last, np = ++tot; 18 last = np; 19 len[np] = len[p] + 1; 20 cnt[np] = 1; 21 right[np] = id; 22 while(p && !tr[p][f]) { 23 tr[p][f] = np; 24 p = fail[p]; 25 } 26 if(!p) { 27 fail[np] = 1; 28 } 29 else { 30 int Q = tr[p][f]; 31 if(len[Q] == len[p] + 1) { 32 fail[np] = Q; 33 } 34 else { 35 int nQ = ++tot; 36 len[nQ] = len[p] + 1; 37 fail[nQ] = fail[Q]; 38 fail[Q] = fail[np] = nQ; 39 memcpy(tr[nQ], tr[Q], sizeof(tr[Q])); 40 while(tr[p][f] == Q) { 41 tr[p][f] = nQ; 42 p = fail[p]; 43 } 44 } 45 } 46 return; 47 } 48 49 void changeMin(int L, int R, int v, int l, int r, int o) { 50 if(L <= l && r <= R) { 51 small[o] = std::min(small[o], v); 52 return; 53 } 54 int mid = (l + r) >> 1; 55 if(L <= mid) changeMin(L, R, v, l, mid, o << 1); 56 if(mid < R) changeMin(L, R, v, mid + 1, r, o << 1 | 1); 57 return; 58 } 59 60 void change(int L, int R, int v, int l, int r, int o) { 61 if(L <= l && r <= R) { 62 tag[o] = std::min(tag[o], v); 63 return; 64 } 65 int mid = (l + r) >> 1; 66 if(L <= mid) change(L, R, v, l, mid, o << 1); 67 if(mid < R) change(L, R, v, mid + 1, r, o << 1 | 1); 68 return; 69 } 70 71 inline void sort() { 72 for(int i = 1; i <= tot; i++) { 73 bin[len[i]]++; 74 } 75 for(int i = 1; i <= tot; i++) { 76 bin[i] += bin[i - 1]; 77 } 78 for(int i = 1; i <= tot; i++) { 79 topo[bin[len[i]]--] = i; 80 } 81 for(int i = tot; i >= 2; i--) { 82 int x = topo[i]; 83 if(cnt[x] == 1) { 84 changeMin(right[x] - len[fail[x]], right[x], len[fail[x]] + 1, 1, n, 1); 85 if(len[fail[x]] < len[x] - 1) change(right[x] - len[x] + 1, right[x] - len[fail[x]] - 1, right[x] + 1, 1, n ,1); 86 //printf("change Min %d %d %d ", right[x] - len[fail[x]], right[x], len[fail[x]] + 1); 87 //printf("change %d %d %d ", right[x] - len[x] + 1, right[x] - len[fail[x]] - 1, right[x] + 1); 88 } 89 cnt[fail[x]] += cnt[x]; 90 right[fail[x]] = right[x]; 91 } 92 return; 93 } 94 95 inline void Min(int &a, int b) { 96 a > b ? a = b : 0; 97 return; 98 } 99 100 void cal(int l, int r, int o) { 101 if(l == r) { 102 printf("%d ", std::min(small[o], tag[o] - r)); 103 return; 104 } 105 int ls = o << 1, rs = ls | 1; 106 Min(small[o << 1], small[o]); 107 Min(tag[o << 1], tag[o]); 108 Min(small[o << 1 | 1], small[o]); 109 Min(tag[o << 1 | 1], tag[o]); 110 int mid = (l + r) >> 1; 111 cal(l, mid, o << 1); 112 cal(mid + 1, r, o << 1 | 1); 113 return; 114 } 115 116 int main() { 117 memset(tag, 0x3f, sizeof(tag)); 118 memset(small, 0x3f, sizeof(small)); 119 init(); 120 scanf("%s", str); 121 n = strlen(str); 122 for(int i = 0; i < n; i++) { 123 insert(str[i], i + 1); 124 } 125 sort(); 126 cal(1, n, 1); 127 return 0; 128 }