[NOI2016]优秀的拆分 后缀数组
题面:洛谷
题解:
因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献。
所以子串这个限制相当于是没有的。
所以我们只需要对于每个位置i求出f[i]表示以i为开头的形如BB这样的串的个数,g[i]表示以i为结尾的形如AA这样的串的个数即可。
考虑分别处理这2个数组。
我们可以枚举AA(BB)这样的串中A(B)的长度l,然后把原串每l个字符放在一个块中,在考虑统计答案。
先考虑这样一个问题:
假如固定一个串的结尾,再枚举这个串A的长度,怎样可以判断是否合法?
实际上我们只需要判断我们假定的这个AA串的开头和中间位置(结尾向前走A的长度)的LCP是否可以覆盖开头到中间即可。
然后如果我们已经把原串对于当前枚举的长度l分成了很多块,其实我们就已经可以对与每个块的开头结尾所代表的点对(i, j)判断是否可以产生贡献了。
但是怎么统计 其他没有刚好对应在某个块的开头结尾的点对 的贡献呢?
表示并没有想出来,,,但是感觉有个blog写的很好,,,
推荐一下:[BZOJ]4650 优秀的拆分(Noi2016)
以后彻底搞懂了再来填坑吧。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 301000 5 #define ac 602000 6 #define LL long long 7 #define rev reverse 8 #define mem(x) memset(x, 0, sizeof(x)) 9 10 int T, n, m; 11 int h[ac], sa[ac], p1[ac], p2[ac], b[ac], d[ac]; 12 int rk[ac], p[AC], t[AC], rk1[ac]; 13 int st1[AC][19], st2[AC][19]; 14 int f[AC], g[AC]; 15 LL ans; 16 char s[AC]; 17 18 void init() 19 { 20 for(R i = 1; i <= n; i ++) f[i] = g[i] = rk[i] = 0;//因为有多组数据,所以要全部清空 21 }//mem这么多次还不如for 22 23 inline void upmax(int &a, int b){ 24 if(b > a) a = b; 25 } 26 27 inline int Min(int a, int b){ 28 return (a < b) ? a : b; 29 } 30 31 void pre() 32 { 33 scanf("%s", s + 1), n = strlen(s + 1), m = 127; 34 } 35 36 void ssort() 37 { 38 for(R i = 1; i <= n; i ++) ++ d[p2[i]]; 39 for(R i = 1; i <= m; i ++) d[i] += d[i - 1]; 40 for(R i = 1; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配d[p2[i]]的排名 41 for(R i = 0; i <= m; i ++) d[i] = 0; 42 43 for(R i = 1; i <= n; i ++) ++ d[p1[i]]; 44 for(R i = 1; i <= m; i ++) d[i] += d[i - 1]; 45 for(R i = n; i; -- i) sa[d[p1[b[i]]] --] = b[i];//给b[i]分配d[p1[b[i]]]的排名 46 for(R i = 0; i <= m; i ++) d[i] = 0; 47 } 48 49 void get_sa() 50 { 51 for(R i = 1; i <= n; i ++) sa[i] = i, rk[i] = s[i];//初始化 52 m = 127;//这个也要重置 53 for(R k = 1; k <= n; k <<= 1) 54 { 55 for(R i = 1; i <= n; i ++) p1[i] = rk[i], p2[i] = rk[i + k]; 56 ssort(); 57 int tmp = 1; 58 rk[sa[1]] = 1; 59 for(R i = 2; i <= n; i ++) 60 rk[sa[i]] = (p1[sa[i]] == p1[sa[i - 1]] && p2[sa[i]] == p2[sa[i - 1]]) ? tmp : ++ tmp; 61 if(tmp >= n) break; 62 m = tmp;//忘了,,, 63 } 64 } 65 66 void build()//获取h数组 67 { 68 //memset(h, 0, sizeof(h)); 69 for(R i = 1, k = 0; i <= n; i ++) 70 { 71 if(k) -- k; 72 int j = sa[rk[i] - 1]; 73 while(s[i + k] == s[j + k]) ++ k; 74 h[rk[i]] = k; 75 } 76 } 77 78 #define st st1 79 void build1()//建st1(维护LCP) 80 { 81 int tmp = 1, cnt = 0; 82 memcpy(rk1, rk, sizeof(rk)); 83 for(R i = 1; i <= n; i ++) 84 { 85 st[i][0] = h[i]; 86 if(i == tmp << 1) tmp <<= 1, ++ cnt; 87 p[i] = tmp, t[i] = cnt; 88 } 89 } 90 #undef st 91 92 void build2()//建st2(维护LCS)改成st1, st2一起建了。。。。 93 { 94 for(R i = 1; i <= n; i ++) st2[i][0] = h[i]; 95 int tmp = 1; 96 for(R i = 1; i <= 18; i ++) 97 { 98 for(R j = 1; j <= n; j ++) 99 { 100 st1[j][i] = Min(st1[j][i - 1], st1[j + tmp][i - 1]); 101 st2[j][i] = Min(st2[j][i - 1], st2[j + tmp][i - 1]); 102 } 103 tmp <<= 1; 104 } 105 } 106 107 inline void swap(int &l, int &r) 108 { 109 int x = l; 110 l = r, r = x; 111 } 112 113 int get1(int l, int r)//查询串l和串r的LCP 114 { 115 l = rk1[l], r = rk1[r]; 116 if(l > r) swap(l, r); 117 ++ l; 118 int len = r - l + 1; 119 return Min(st1[l][t[len]], st1[r - p[len] + 1][t[len]]); 120 } 121 122 int get2(int l, int r)//查询串l和串r的LCS 123 {//因为是翻转过来求的,所以查询要翻转一下 124 l = n - l + 1, r = n - r + 1; 125 l = rk[l], r = rk[r]; 126 if(l > r) swap(l, r); 127 ++ l; 128 int len = r - l + 1; 129 return Min(st2[l][t[len]], st2[r - p[len] + 1][t[len]]); 130 } 131 132 void get() 133 { 134 int lim = n << 1; 135 for(R k = 1; k < lim; k ++)//枚举A的长度 136 { 137 int maxn = 0, maxn2 = 0; 138 for(R i = 1; i <= n; i += k) 139 { 140 int j = i + k;//j为下一段开头 141 if(j > n) break; 142 if(i > maxn) 143 { 144 int lcp = get1(i, j), lcs = get2(i, j); 145 maxn = i + lcp - 1; 146 int l = i - lcs + 1, r = j + lcp - 2 * k; 147 if(lcp + lcs > k) ++ f[l], -- f[r + 1]; 148 } 149 if(i > maxn2) 150 { 151 int lcp = get2(n - i + 1, n - j + 1), lcs = get1(n - i + 1, n - j + 1); 152 maxn2 = i + lcp - 1; 153 int l = i - lcs + 1, r = j + lcp - 2 * k; 154 if(lcp + lcs > k) ++ g[l], -- g[r + 1]; 155 } 156 } 157 } 158 for(R i = 1; i <= n; i ++) f[i] += f[i - 1], g[i] += g[i - 1]; 159 rev(g + 1, g + n + 1); 160 } 161 162 void work() 163 { 164 ans = 0;//f是开头 165 for(R i = 1; i <= n; i += 2) 166 ans += 1LL * f[i] * g[i - 1] + ((i + 1 > n) ? 0 : 1LL * f[i + 1] * g[i]); 167 printf("%lld\n", ans); 168 } 169 170 int main() 171 { 172 // freopen("in.in", "r", stdin); 173 scanf("%d", &T); 174 while(T --) 175 { 176 init(); 177 pre(); 178 get_sa(); 179 build(); 180 build1();//建st(维护LCP) 181 rev(s + 1, s + n + 1);//翻转 182 // memset(rk, 0, sizeof(rk));//因为对于单组数据而言,长度不变,所以rk不必再次清空 183 get_sa(); 184 build(); 185 build2();//建st2(维护LCS) 186 get(); 187 work(); 188 } 189 // fclose(stdin); 190 return 0; 191 }