如果乙個字串可以被拆分為\(aabb\)的形式,其中\(a\)和\(b\)是任意非空字串,則我們稱該字串的這種拆分是優秀的。
例如,對於字串\(aabaabaa\),如果令\(a=aab\),\(b=a\),我們就找到了這個字串拆分成\(aabb\)的一種方式。
乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令\(a=a\),\(b=baa\),也可以用\(aabb\)表示出上述字串;但是,字串\(abaabaa\)就沒有優秀的拆分。
現在給出乙個長度為\(n\)的字串\(s\),我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這裡的子串是指字串中連續的一段。
以下事項需要注意:
\(1\). 出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。
\(2\). 在乙個拆分中,允許出現\(a=b\)。例如\(cccc\)存在拆分\(a=b=c\)。
\(3\). 字串本身也是它的乙個子串。
第一行乙個整數\(t\),表示資料的組數。保證\(1\le t\le 10\)。
接下來\(t\)行,每行乙個僅由英文小寫字母構成的字串\(s\),意義如題所述。
輸出\(t\)行,每行乙個整數,表示字串\(s\)所有子串的所有拆分中,總共有多少個是優秀的拆分。
4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
354
7
我們用\(s_\)表示字串\(s\)第\(i\)個字元到第\(j\)個字元的子串(從\(1\)開始計數)。
第一組資料中,共有\(3\)個子串存在優秀的拆分:
\(s_=aabb\),優秀的拆分為\(a=a\),\(b=b\);
\(s_=bbbb\),優秀的拆分為\(a=b\),\(b=b\);
\(s_=aabbbb\),優秀的拆分為\(a=a\),\(b=bb\)。
而剩下的子串不存在優秀的拆分,所以第一組資料的答案是\(3\)。
第二組資料中,有兩類,總共\(4\)個子串存在優秀的拆分:
對於子串\(s_=s_=s_=cccc\),它們優秀的拆分相同,均為\(a=c\),\(b=c\),但由於這些子串位置不同,因此要計算\(3\)次;
對於子串\(s_=cccccc\),它優秀的拆分有\(2\)種:\(a=c\),\(b=cc\)和 \(a=cc\),\(b=c\),它們是相同子串的不同拆分,也都要計入答案。
所以第二組資料的答案是\(3+2=5\)。
第三組資料中,\(s_\)和\(s_\)各有\(2\)種優秀的拆分,其中\(s_\)是問題描述中的例子,所以答案是\(2+2=4\)。
第四組資料中,\(s_\),\(s_\),\(s_\),\(s_\),\(s_\)各有 \(1\)種優秀的拆分,\(s_\)有\(2\)種優秀的拆分,所以答案是\(5+2=7\)。
對於全部的測試點,保證\(1\le t\le 10\)。以下對資料的限制均是對於單組輸入資料而言的,也就是說同乙個測試點下的\(t\)組資料均滿足限制條件。
我們假定\(n\)為字串\(s\)的長度,每個測試點的詳細資料範圍見下表:
由於\(aabb\)是由\(aa\)和\(bb\)組成的,我們可以先對\(aa\)進行考慮,然後再計算兩個連在一起的\(aa\)(即\(aabb\))有多少個。
我們設\(f(i)\)表示最後乙個字母位置為\(i\)的\(aa\)有多少個,\(g(i)\)表示第乙個字母位置為\(i\)的\(aa\)有多少個,則最終答案為\(\sum_^f(i)\times g(i+1)\)。
我們要列舉\(aa\)的個數,顯然先要列舉\(aa\)的長度,即\(a\)的長度的兩倍,因此列舉\(a\)的長度即可。為了使時間複雜度降到\(o(n\ log\ n)\),我們考慮這樣的做法:
每次列舉乙個長度\(length\),然後將\(s\)從頭開始劃分出\(\lfloor\frac\rfloor\)個連續的長度為\(length\)的區間。對於乙個區間,設其第乙個字母位置為\(i\),最後乙個字母位置為\(j\),我們計算\(s_\)和\(s_\)的最長公共字尾(設其長度為\(lcs\))以及\(s_\)和\(s_\)的最長公共字首(設其長度為\(lcp\))。若\(length>lcs+lcp\),顯然不可能存在長度為\(2\times length\)的\(aa\)包含該區間。相反的,若\(length\le lcs+lcp\),一定存在且僅存在\(lcs+lcp-length+1\)個長度為\(2\times length\)的\(aa\)包含該區間,分別是\(s_,s_,\cdots,s_\),對它們更新\(f\)值和\(g\)值即可。求解兩個字串的\(lcp\)和\(lcs\)(兩個字串反轉之後的\(lcp\))等問題通常需要使用字尾陣列。
我們先利用字尾陣列計算\(s_\)和\(s_\)的\(lcp\)(\(1\le i),然後根據倍增思想,使用st表輔助計算\(s_\)和\(s_\)的\(lcp\)(\(1\le i,j\le n,i\neq j\))。
由於包含上述列舉的區間的\(aa\)是連續的,我們可以使用差分法對\(f\)值和\(g\)值進行更新。
#include#include#include#includeusing namespace std;
const int maxn=(int)3e4+5,maxm=15;
int sta[maxn][maxm],stb[maxn][maxm];
int raka[maxn],rakb[maxn],saa[maxn],sab[maxn],hgta[maxn],hgtb[maxn];
int lg[maxn],sum[maxn],sa[maxn],rak[maxn],f[maxn],g[maxn];
char s[maxn];
int n;
inline void iit(int(*st)[maxm],int*rak,int*sa,int*hgt)
}for(register int i=2;i<=n;i++)
for(register int i=1;i
ans+=1ll*f[i]*g[i+1];
printf("%lld\n",ans);
memset(raka,0,sizeof(raka));
memset(rakb,0,sizeof(rakb));
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
} return 0;
}
NOI2016 優秀的拆分
看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...
NOI2016 優秀的拆分
題目實際上要求我們求從每個點出發的aa串的數量 考慮點i的答案,發現如果字首i與字首j j i 的最長公共字尾 i j,那麼i點出發向前就存在乙個長度為i j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件 考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的...
NOI2016 優秀的拆分
點此看題 首先轉化問題,我們可以求出a i b i a i b i a i b i 即以i ii結束 開始的aaaa aa串的數量,這樣答案就可以表示為 a i b i 1 sum a i times b i 1 a i b i 1 求這兩個陣列,可以隔距離len lenle n設定乙個點,這樣乙個...