NOI 2016 优秀的拆分 后缀数组
2017-01-24 14:10
267 查看
如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
现在给出一个长度为 n(n < 30000)的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
1)出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
2)在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。
3)字符串本身也是它的一个子串。
思路:
这题很久以前就想做了(当时写了个暴力就跑了),这几天复习后缀数组就正好拿来做一做。
这题的重点其实就是统计字符串中形如AA的字符串个数,还是有不小思维难度的~~~
首先枚举A的长度l,再枚举一个k,i = k * l, j = (k + 1) * l,设后缀 i 和后缀 j 的LCP为x,前缀 i - 1 和前缀 j - 1 的最长公共后缀为y,则当x + y = l时,我们恰能找到[i - y, j + x - 1]为一个合法的AA串;那么当x + y > l 时,我们就能找到一个合法的串区间(有不少细节,建议自己推一推)。用差分序列加上答案,最后做前缀和就能得到每一个位置起始以及终止的AA串个数,答案就很好统计了。
另外数组注意清空,最后由于这个WA了几次(当然这与后缀数组的写法有关)。
代码:
例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
现在给出一个长度为 n(n < 30000)的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
1)出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
2)在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。
3)字符串本身也是它的一个子串。
思路:
这题很久以前就想做了(当时写了个暴力就跑了),这几天复习后缀数组就正好拿来做一做。
这题的重点其实就是统计字符串中形如AA的字符串个数,还是有不小思维难度的~~~
首先枚举A的长度l,再枚举一个k,i = k * l, j = (k + 1) * l,设后缀 i 和后缀 j 的LCP为x,前缀 i - 1 和前缀 j - 1 的最长公共后缀为y,则当x + y = l时,我们恰能找到[i - y, j + x - 1]为一个合法的AA串;那么当x + y > l 时,我们就能找到一个合法的串区间(有不少细节,建议自己推一推)。用差分序列加上答案,最后做前缀和就能得到每一个位置起始以及终止的AA串个数,答案就很好统计了。
另外数组注意清空,最后由于这个WA了几次(当然这与后缀数组的写法有关)。
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define For(i,j,k) for(int i = j;i <= k;i++) #define Forr(i,j,k) for(int i = j;i >= k;i--) #define Set(i,j) memset(i, j, sizeof(i)) using namespace std; const int N = 30010; int n, Log , pre , suf ; struct SuffixArray{ int sa[N<<1], rank , h [18], T1 , T2 , c ; char S ; void init(){ Set(S, 0), Set(c, 0), Set(T1, 0), Set(T2, 0); Set(h, 0), Set(sa, 0), Set(rank, 0); } void buildsa(int m = 'z'){ int *x = T1, *y = T2; For(i,1,n) c[x[i] = S[i]]++; For(i,1,m) c[i] += c[i-1]; Forr(i,n,1) sa[c[x[i]]--] = i; for(int k = 1;k < n;k <<= 1){ int p = 0; For(i,n-k+1,n) y[++p] = i; For(i,1,n) if(sa[i] > k) y[++p] = sa[i] - k; For(i,1,m) c[i] = 0; For(i,1,n) c[x[y[i]]]++; For(i,1,m) c[i] += c[i-1]; Forr(i,n,1) sa[c[x[y[i]]]--] = y[i]; swap(x, y); p = x[sa[1]] = 1; For(i,2,n) x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i] + k] == y[sa[i-1] + k] ? p : ++p; if(p >= n) break; m = p; } } void buildheight(){ int p = 0; For(i,1,n) rank[sa[i]] = i; For(i,1,n){ int j = sa[rank[i] + 1]; if(p) p--; if(!j) continue; while(S[i + p] == S[j + p]) ++p; h[rank[i]][0] = p; } For(j,1,16) for(int i = 1;i + (1 << j) <= n;i++) h[i][j] = min(h[i][j-1], h[i + (1 << (j - 1))][j-1]); } int LCP(int x, int y){ x = rank[x], y = rank[y]; if(x > y) swap(x, y); int t = Log[y - x]; return min(h[x][t], h[y - (1 << t)][t]); } }A, B; int main(){ For(i,2,N-1) Log[i] = Log[i >> 1] + 1; int T; scanf("%d", &T); while(T--){ A.init(), B.init(); Set(pre, 0), Set(suf, 0); scanf("%s", A.S + 1); n = strlen(A.S + 1); A.buildsa(); For(i,1,n) B.S[i] = A.S[n + 1 - i]; B.buildsa(); A.buildheight(); B.buildheight(); For(l,1,n/2) for(int i = l, j = i + l;j <= n;i += l, j += l){ int R = min(A.LCP(i, j), l), L = min(B.LCP(n + 1 - (i - 1), n + 1 - (j - 1)), l - 1); if(L + R >= l){ suf[j - L - l]++, suf[i + R - l + 1]--; pre[j - L + l - 1]++, pre[i + R + l]--; } } For(i,1,n) pre[i] += pre[i-1], suf[i] += suf[i-1]; long long Ans = 0; For(i,1,n) Ans += pre[i] * suf[i+1]; printf("%lld\n", Ans); } return 0; }
相关文章推荐
- [后缀数组] BZOJ4650: [Noi2016] 优秀的拆分
- 字符串(后缀自动机):NOI 2016 优秀的拆分
- [后缀数组 枚举 字符串分段] BZOJ 4650 [Noi2016]优秀的拆分
- NOI2016 优秀的拆分 [后缀数组]
- [NOI 2016]优秀的拆分
- BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】
- bzoj 4650: [Noi2016]优秀的拆分 后缀数组
- 【后缀数组】[NOI2016]优秀的拆分
- NOI2016 优秀的拆分 后缀数组
- 【uoj219】 NOI2016—优秀的拆分
- Noi2016 D1 T1 优秀的拆分 90做法
- [NOI 2016] 优秀的拆分
- 【UOJ 219】【BZOJ 4650】【NOI 2016】优秀的拆分(后缀数组)
- 【BZOJ 4650】【UOJ #219】【NOI 2016】优秀的拆分
- UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
- [BZOJ4650][NOI2016]优秀的拆分-后缀数组
- BZOJ.4566.[HAOI2016]找相同字符(后缀数组 单调栈)
- 洛谷P1117:[NOI2016]优秀的拆分(后缀自动机)
- BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)
- 【BZOJ4516】【Sdoi2016】生成魔咒 后缀数组 线段树