您的位置:首页 > 其它

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了几次(当然这与后缀数组的写法有关)。

代码:

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: