题目链接:洛谷点我:-) 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’形式的串有多少个,那么答案就是
我们枚举一个长度
记
如果
(自己可以举个例子看看,如’abcabcab‘)
为了方便理解,假设
但是每次有一个连续区间,我们不能一个一个加上,因为时效会出问题。所以用到差分的思想(当然有闲心写线段树也是可以的),在区间开始的地方加一,在区间结束的后一个位置减一,那么最后做一遍前缀和即可。
注意:每次要清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; }
来源:https://www.cnblogs.com/miaomiao1220/p/6642347.html