1. 程式人生 > >[NOI2016]優秀的拆分 後綴數組

[NOI2016]優秀的拆分 後綴數組

max cli line one 我們 https long long 。。 pen

題面:洛谷

題解:

  因為對於原串的每個長度不一定等於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 }
View Code

[NOI2016]優秀的拆分 後綴數組