[NOI2016]优秀的拆分

题意

一串字符串的子串能够被拆分成不重叠的$AABB$($A, B$为该子串的子串)的方案数

题解

考虑对子串进行隔离处理,枚举隔离长度$l$

那么在此隔离长度下若存在子串满足$AABB$,那么它必定横跨两个隔离点,那么此时求出每相邻两个隔离点的最长公共前缀$x$和最长公共后缀$y$(跑两遍$SA$求),那么若$x + y >= l$,那么必定可以构成$AABB$的子串(画下图就知道了),那么再统计数量,找到满足该条件的起始位置和终止位置,用差分就好了

代码

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <vector>
  7 
  8 using namespace std;
  9 
 10 typedef long long LL;
 11 
 12 const int MAXN = 3e04 + 10;
 13 
 14 int T;
 15 
 16 int N;
 17 
 18 struct Suffix_Array {
 19     char Str[MAXN];
 20     int M = 122;
 21     int SA[MAXN]= {0};
 22     int Fir[MAXN]= {0}, Srk[MAXN]= {0};
 23     int buck[MAXN]= {0};
 24     void SA_Requirement () {
 25         M = 122;
 26         for (int i = 1; i <= M; i ++)
 27             buck[i] = 0;
 28         for (int i = 1; i <= N; i ++)
 29             buck[Fir[i] = Str[i]] ++;
 30         for (int i = 2; i <= M; i ++)
 31             buck[i] += buck[i - 1];
 32         for (int i = N; i >= 1; i --)
 33             SA[buck[Fir[i]] --] = i;
 34         for (int k = 1; k <= N; k <<= 1) {
 35             int p = 0;
 36             for (int i = N - k + 1; i <= N; i ++)
 37                 Srk[++ p] = i;
 38             for (int i = 1; i <= N; i ++)
 39                 if (SA[i] > k)
 40                     Srk[++ p] = SA[i] - k;
 41             for (int i = 1; i <= M; i ++)
 42                 buck[i] = 0;
 43             for (int i = 1; i <= N; i ++)
 44                 buck[Fir[i]] ++;
 45             for (int i = 2; i <= M; i ++)
 46                 buck[i] += buck[i - 1];
 47             for (int i = N; i >= 1; i --)
 48                 SA[buck[Fir[Srk[i]]] --] = Srk[i], Srk[i] = 0;
 49             swap (Fir, Srk);
 50             Fir[SA[1]] = 1, p = 1;
 51             for (int i = 2; i <= N; i ++)
 52                 Fir[SA[i]] = (Srk[SA[i]] == Srk[SA[i - 1]] && Srk[SA[i] + k] == Srk[SA[i - 1] + k]) ? p : ++ p;
 53             if (p == N)
 54                 break;
 55             M = p;
 56         }
 57     }
 58     int Rank[MAXN]= {0};
 59     int Height[MAXN]= {0};
 60     void Height_Array () {
 61         for (int i = 1; i <= N; i ++)
 62             Rank[SA[i]] = i;
 63         int k = 0;
 64         for (int i = 1; i <= N; i ++) {
 65             if (k)
 66                 k --;
 67             int j = SA[Rank[i] - 1];
 68             while (j + k <= N && i + k <= N && Str[j + k] == Str[i + k])
 69                 k ++;
 70             Height[Rank[i]] = k;
 71         }
 72     }
 73 
 74     int ST[MAXN][20];
 75     void RMQ () {
 76         for (int i = 1; i <= N; i ++)
 77             ST[i][0] = Height[i];
 78         for (int j = 1; j <= 18; j ++)
 79             for (int i = 1; i + (1 << j) - 1 <= N; i ++)
 80                 ST[i][j] = min (ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
 81     }
 82     int Query (int l, int r) {
 83         l ++;
 84         int k = log2 (r - l + 1);
 85         return min (ST[l][k], ST[r - (1 << k) + 1][k]);
 86     }
 87     int LCP (int x, int y) {
 88         if (Rank[x] > Rank[y])
 89             swap (x, y);
 90         return Query (Rank[x], Rank[y]);
 91     }
 92     void init () {
 93         memset (SA, 0, sizeof (SA));
 94         memset (Height, 0, sizeof (Height));
 95         memset (Rank, 0, sizeof (Rank));
 96         memset (Fir, 0, sizeof (Fir));
 97         memset (Srk, 0, sizeof (Srk));
 98     }
 99 } For, Rev;
100 
101 int endwith[MAXN]= {0}, startwith[MAXN]= {0};
102 
103 int main () {
104     scanf ("%d", & T);
105     for (int Case = 1; Case <= T; Case ++) {
106         memset (endwith, 0, sizeof (endwith));
107         memset (startwith, 0, sizeof (startwith));
108         For.init (), Rev.init ();
109         scanf ("%s", For.Str + 1);
110         N = strlen (For.Str + 1);
111         for (int i = 1; i <= N; i ++)
112             Rev.Str[i] = For.Str[N - i + 1];
113         For.SA_Requirement (), For.Height_Array (), For.RMQ ();
114         Rev.SA_Requirement (), Rev.Height_Array (), Rev.RMQ ();
115         LL ans = 0;
116         for (int l = 1; l <= (N >> 1); l ++)
117             for (int i = l, j = (l << 1); j <= N; i += l, j += l) {
118                 int lcpfor = min (For.LCP (i, j), l);
119                 int lcprev = min (Rev.LCP (N - j + 1, N - i + 1), l);
120                 if (lcpfor + lcprev - 1 >= l) {
121                     endwith[j + l - lcprev] ++;
122                     endwith[j + lcpfor] --;
123                     startwith[i - lcprev + 1] ++;
124                     startwith[i - (l - lcpfor - 1)] --;
125                 }
126             }
127         for (int i = 1; i <= N; i ++) {
128             endwith[i] += endwith[i - 1];
129             startwith[i] += startwith[i - 1];
130         }
131         for (int i = 1; i <= N; i ++)
132             ans += (LL) endwith[i] * startwith[i + 1];
133         printf ("%lld\n", ans);
134     }
135 
136     return 0;
137 }
138 
139 /*
140 4
141 aabbbb
142 cccccc
143 aabaabaabaa
144 bbaabaababaaba
145 */
146 
147 /*
148 1
149 aabbbb
150 */
151 
152 /*
153 10
154 zzzzzzzzzzzzzzzzzzzzzzzzzzz
155 icicicicicicicicicicic
156 saasaasaasaasaasaasaa
157 znunznunznunznunznun
158 ttfhhttfhhttfhhttfhhttfhh
159 fqxqblfqxqblfqxqblfqxqblfqxqbl
160 xxpruxxpruxxpruxxpru
161 mpheqsmpheqsmpheqsmpheqs
162 ptvbemqptvbemqptvbemqptvbemq
163 nxykqknxykqknxykqknxykqk
164 */
165 
166 /*
167 1
168 mpheqsmpheqsmpheqsmpheqs
169 */
View Code

 

posted @ 2018-12-07 11:06  Colythme  阅读(100)  评论(0编辑  收藏  举报