题目链接:洛谷点我:-) UOJ点我:-)
题目描述:
如果一个字符串可以被拆分为 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.字符串本身也是它的一个子串。
输入格式:
每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据的组数。保证 1≤T≤10。
接下来 T行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题所述。
输出格式:
输出 T行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中,总共有多少个是优秀的拆分。
思路:
求两个数组:st[i]与en[i],分别表示以i这个字符开头与以i这个字符结尾的‘AA’形式的串有多少个,那么答案就是∑n−1i=1st[i+1]∗en[i],难点在于如何求出这两个数组:
我们枚举一个长度L表示当前找的‘AA’型串的长度的一半,枚举i=k∗L,j=i+L,
记x=lcp(suf(i),suf(j)), 记y=lcs(pre(i−1),pre(j−1)),
如果x+y>=L,记t=x+y−L+1,表示我们找到了t个长度为2L的’AA‘串
(自己可以举个例子看看,如’abcabcab‘)
为了方便理解,假设x+y=L,那么我们恰好找到一个(i−y,j+x−1)的’AA‘串。
但是每次有一个连续区间,我们不能一个一个加上,因为时效会出问题。所以用到差分的思想(当然有闲心写线段树也是可以的),在区间开始的地方加一,在区间结束的后一个位置减一,那么最后做一遍前缀和即可。
注意:每次要清c1, c2(即x, y)数组,因为后面的“y[sa[i]+k]==y[sa[i-1]+k]”可能会越限
感想:
这题最开始用kmp写了个95分暴力,觉得可以了,就没做了。然后某一天发现小伙伴都写了满分啊,吓得我赶紧写
真心想不出正解,然而网上的题解又很敷衍,幸亏有DY大神的帮助,不然我就做不出来了qwq。有点难理解,其实也还好啦。但是。。。正解是怎么想到的qwq
毕竟代码不难写,所以妙妙以后要多练思维(-_-|||)
想想,觉得本题也可以用hash解决,找个时间写写把代码再贴上来吧。。。
代码:
95分大暴力
//miaomiao 2016.8.2
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXS (2000+5)
int f[MAXS][MAXS];
vector<int> xh[MAXS];
void init(){
for(int i = 0; i < MAXS; i++) xh[i].clear();
}
void get_fail(int now, char *s){
f[now][0] = f[now][1] = 0;
int slen = strlen(s);
for(int i = 1; i < slen; i++){
int j = f[now][i];
while(j && s[i] != s[j]) j = f[now][j];
f[now][i+1] = s[i]==s[j]? j+1: 0;
}
}
char s[MAXS];
int main(){
int T;
scanf("%d", &T);
while(T--){
init();
scanf("%s", s);
int slen = strlen(s);
for(int i = 0; i < slen; i++){
get_fail(i, s+i);
for(int j = 1; j <= (slen-i); j++){
if(j % (j-f[i][j])) continue;
int p = j / (j-f[i][j]);
if(p % 2) continue;
xh[i].push_back((j+1)/2);
}
}
int ans = 0;
for(int i = 0; i < slen; i++){
for(int j = 0; j < xh[i].size(); j++){
int len = xh[i][j];
int startB = i+len*2;
ans += xh[startB].size();
}
}
printf("%d\n", ans);
}
return 0;
}
看了题解的正解
//miaomiao 2017.1.28
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)
#define Forr(i, a, b) for(int i = (a); i >= (int)(b); i--)
#define LOG (15+5)
#define MAXN (30000+5)
int n, c[MAXN], c1[MAXN], c2[MAXN], Log[MAXN];
struct SuffixArray{
char s[MAXN];
int sa[MAXN], h[MAXN][LOG], rank[MAXN];
void init(){
Set(c1, 0); Set(c2, 0);
}
void buildsa(int m='z'){
int *x = c1, *y = c2;
For(i, 1, m) c[i] = 0;
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;
int p;
for(int k = 1; k <= n; k <<= 1){
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 getheight(){
For(i, 1, n) rank[sa[i]] = i;
int k = 0, j;
For(i, 1, n){
if(k) k--;
j = sa[rank[i]+1];
if(rank[i] == n) continue;
while(s[i+k] == s[j+k]) k++;
h[rank[i]][0] = k;
}
For(j, 1, 15) For(i, 1, n){
if(i+(1<<(j-1)) > n) break;
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 k = Log[y-x];
return min(h[x][k], h[y-(1<<k)][k]);
}
}A, B;
int st[MAXN], en[MAXN];
int main(){
For(i, 2, MAXN-1) Log[i] = Log[i>>1]+1;
int T;
scanf("%d", &T);
while(T--){
A.init(); B.init();
Set(st, 0); Set(en, 0);
scanf("%s", A.s+1);
n = strlen(A.s+1);
For(i, 1, n) B.s[i] = A.s[n-i+1];
A.buildsa(); A.getheight();
B.buildsa(); B.getheight();
int j, x, y, t;
For(L, 1, n/2){
for(int i=L, j=i+L; j <= n; i+=L,j+=L){
x = min(A.LCP(i,j), L), y = min(B.LCP(n-(i-1)+1, n-(j-1)+1), L-1);
t = x+y-L+1;
if(x+y >= L){
st[i-y]++; st[i-y+t]--;
en[j+x-t]++; en[j+x]--;
}
}
}
For(i, 1, n) st[i]+=st[i-1], en[i]+=en[i-1];
LL ans = 0;
For(i, 1, n) ans += 1LL*en[i]*st[i+1];
printf("%lld\n", ans);
}
return 0;
}