[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 }
View Code

 

posted @ 2018-12-04 22:38  ww3113306  阅读(189)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。