NOI2016 優秀的拆分

2022-05-26 19:48:12 字數 3338 閱讀 4426

如果乙個字串可以被拆分為\(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設定乙個點,這樣乙個...