本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!
Description
如果一個字符串可以被拆分為 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,則我們稱該字符串的這種拆
分是優秀的。例如,對於字符串 aabaabaa,如果令 A=aabA=aab,B=aB=a,我們就找到了這個字符串拆分成 AABBA
ABB 的一種方式。一個字符串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 A=aA=a,B=baa
B=baa,也可以用 AABBAABB 表示出上述字符串;但是,字符串 abaabaa 就沒有優秀的拆分。現在給出一個長度為
nn 的字符串 SS,我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這里的子串是指字符串
中連續的一段。
以下事項需要注意:
出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。在一個拆分中,允許出現
A=BA=B。例如 cccc 存在拆分 A=B=cA=B=c。字符串本身也是它的一個子串。
Input
每個輸入文件包含多組數據。輸入文件的第一行只有一個整數 TT,表示數據的組數。保證 1≤T≤10。接
下來 TT 行,每行包含一個僅由英文小寫字母構成的字符串 SS,意義如題所述。N<=≤30000
Output
輸出 TT 行,每行包含一個整數,表示字符串 SS 所有子串的所有拆分中,總共有多少個是優秀的拆分。
Sample Input
4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
Sample Output
3
5
4
7
explanation
我們用 S[i,j]S[i,j] 表示字符串 SS 第 ii 個字符到第 jj 個字符的子串(從 11 開始計數)。
第一組數據中,共有 33 個子串存在優秀的拆分:
S[1,4]=aabbS[1,4]=aabb,優秀的拆分為 A=aA=a,B=bB=b;
S[3,6]=bbbbS[3,6]=bbbb,優秀的拆分為 A=bA=b,B=bB=b;
S[1,6]=aabbbbS[1,6]=aabbbb,優秀的拆分為 A=aA=a,B=bbB=bb。
而剩下的子串不存在優秀的拆分,所以第一組數據的答案是 33。
第二組數據中,有兩類,總共 44 個子串存在優秀的拆分:
對於子串 S[1,4]=S[2,5]=S[3,6]=ccccS[1,4]=S[2,5]=S[3,6]=cccc,它們優秀的拆分相同,均為 A=cA=c,B=cB=c,但由於這些子串位置不同,因此要計算 33 次;
對於子串 S[1,6]=ccccccS[1,6]=cccccc,它優秀的拆分有 22 種:A=cA=c,B=ccB=cc 和 A=ccA=cc,B=cB=c,它們是相同子串的不同拆分,也都要計入答案。
所以第二組數據的答案是 3+2=53+2=5。
第三組數據中,S[1,8]S[1,8] 和 S[4,11]S[4,11] 各有 22 種優秀的拆分,其中 S[1,8]S[1,8] 是問題描述中的例子,所以答案是 2+2=42+2=4。
第四組數據中,S[1,4]S[1,4],S[6,11]S[6,11],S[7,12]S[7,12],S[2,11]S[2,11],S[1,8]S[1,8] 各有 11 種優秀的拆分,S[3,14]S[3,14] 有 22 種優秀的拆分,所以答案是 5+2=75+2=7。
5
4
7
explanation
我們用 S[i,j]S[i,j] 表示字符串 SS 第 ii 個字符到第 jj 個字符的子串(從 11 開始計數)。
第一組數據中,共有 33 個子串存在優秀的拆分:
S[1,4]=aabbS[1,4]=aabb,優秀的拆分為 A=aA=a,B=bB=b;
S[3,6]=bbbbS[3,6]=bbbb,優秀的拆分為 A=bA=b,B=bB=b;
S[1,6]=aabbbbS[1,6]=aabbbb,優秀的拆分為 A=aA=a,B=bbB=bb。
而剩下的子串不存在優秀的拆分,所以第一組數據的答案是 33。
第二組數據中,有兩類,總共 44 個子串存在優秀的拆分:
對於子串 S[1,4]=S[2,5]=S[3,6]=ccccS[1,4]=S[2,5]=S[3,6]=cccc,它們優秀的拆分相同,均為 A=cA=c,B=cB=c,但由於這些子串位置不同,因此要計算 33 次;
對於子串 S[1,6]=ccccccS[1,6]=cccccc,它優秀的拆分有 22 種:A=cA=c,B=ccB=cc 和 A=ccA=cc,B=cB=c,它們是相同子串的不同拆分,也都要計入答案。
所以第二組數據的答案是 3+2=53+2=5。
第三組數據中,S[1,8]S[1,8] 和 S[4,11]S[4,11] 各有 22 種優秀的拆分,其中 S[1,8]S[1,8] 是問題描述中的例子,所以答案是 2+2=42+2=4。
第四組數據中,S[1,4]S[1,4],S[6,11]S[6,11],S[7,12]S[7,12],S[2,11]S[2,11],S[1,8]S[1,8] 各有 11 種優秀的拆分,S[3,14]S[3,14] 有 22 種優秀的拆分,所以答案是 5+2=75+2=7。
正解:二分+哈希 or 后綴數組
解題報告:
考慮只需求出以每個點開頭的$AA$串和每個點結尾的$AA$串,然后相鄰位置組合即可得到答案。
那么我們考慮每次枚舉一個長度$L$,然后每隔$L$建一個關鍵點,容易發現,每個長度為$L*2$的$AA$串,一定會經過兩個關鍵點,觀察相鄰兩個關鍵點的關系, 發現我們需要求出相鄰兩個關鍵點往前拓展的$LCS$和往后拓展的$LCP$,組合之后可以得到一個$A$串,兩個位置對應相等。
根據位置關系,我們可以確定從一段區間的開始都可以得到一個$AA$串。
這樣做的復雜度為調和級數,也就是$O(nlogn)$,加上每次二分得到$LCP$、$LCS$的復雜度就是兩個$log$。
同時因為有多次修改,而只需查詢一次,我們用差分維護即可。
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXN = 50011;
const int MOD = 30000007;
int n;
char ch[MAXN];
LL hash[MAXN],mo[MAXN],u[MAXN],v[MAXN],ans;
//u[i]記錄i開頭的AA的方案數,v[i]記錄i結尾的AA的方案數,均為差分數組
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline LL gethash(int l,int r){
LL now=hash[l]-hash[r]*mo[r-l];
now%=MOD; now+=MOD; now%=MOD;
return now;
}
inline void work(){
int T=getint(); mo[0]=1; for(int i=1;i<=30000;i++) mo[i]=mo[i-1]*31,mo[i]%=MOD;
while(T--) {
scanf("%s",ch+1); n=strlen(ch+1); int l,r,mid,head,tail,last,pos;
memset(u,0,sizeof(u)); memset(v,0,sizeof(v));
hash[n+1]=0; for(int i=n;i>=1;i--) hash[i]=hash[i+1]*31+ch[i]-'a'+1,hash[i]%=MOD;
for(int L=1;L*2<=n;L++)//枚舉一個A的長度,則AA長度為2*L
for(int i=L+L;i<=n;i+=L) { //和后面的塊求lcp,和前面的塊求lcs
if(ch[i]!=ch[i-L]) continue;
//lcs
l=1; r=L; last=i-L; pos=0;
while(l<=r) {
mid=(l+r)>>1;
if(gethash(last-mid+1,last+1)==gethash(i-mid+1,i+1)) l=mid+1,pos=mid;
else r=mid-1;
}
head=i-pos+1;
//lcp
l=1; r=L; pos=0;
while(l<=r){
mid=(l+r)>>1;
if(gethash(last,last+mid)==gethash(i,i+mid)) l=mid+1,pos=mid;
else r=mid-1;
}
tail=i+pos-1;
head=max(head+L-1,i);
tail=min(tail,i+L-1);
if(head<=tail) {
u[head-L*2+1]++; u[tail+1-L*2+1]--;
v[head]++; v[tail+1]--;
}
}
ans=0;
for(int i=1;i<=n;i++) u[i]+=u[i-1],v[i]+=v[i-1];
for(int i=1;i<n;i++) ans+=v[i]*u[i+1];
printf("%lld\n",ans);
}
}
int main()
{
work();
return 0;
}
