如果乙個字串可以被拆分為 \(\text\) 的形式,其中 \(\text\) 和 \(\text\) 是任意非空字串,則我們稱該字串的這種拆分是優秀的。
例如,對於字串 \(\texttt\) ,如果令 \(\text=\texttt\),\(\text=\texttt\),我們就找到了這個字串拆分成 \(\text\) 的一種方式。
乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。
比如我們令 \(\text=\texttt\),\(\text=\texttt\),也可以用 \(\text\) 表示出上述字串;但是,字串 \(\texttt\) 就沒有優秀的拆分。
現在給出乙個長度為 \(n\) 的字串 \(s\),我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這裡的子串是指字串中連續的一段。
以下事項需要注意:
出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。
在乙個拆分中,允許出現 \(\text=\text\)。例如 \(\texttt\) 存在拆分 \(\text=\text=\texttt\)。
字串本身也是它的乙個子串。
每個輸入檔案包含多組資料。
輸入檔案的第一行只有乙個整數 \(t\),表示資料的組數。
接下來 \(t\) 行,每行包含乙個僅由英文小寫字母構成的字串 \(s\),意義如題所述。
輸出 \(t\) 行,每行包含乙個整數,表示字串 \(s\) 所有子串的所有拆分中,總共有多少個是優秀的拆分。
\(n\le 30000\)
太良心了
\(85\%\)的點\(n\le 500\),直接\(o(n^3)\)暴力列舉區間+斷點用雜湊判斷
然後只要稍微動動腦子:設\(a[i]\)表示以\(i\)結尾的\(\text\)串個數,\(b[i]\)表示以\(i\)開頭的\(\text\)串個數,那麼答案就是\(\sum\limits_^
a[i]*b[i+1]\)
\(o(n^2)\) 95分到手
最後五分如果想不出來不拿也感覺無所謂。。。最後五分確實不好想
所以開始說正解:
上面的95分解法問題就在於\(a[n],\ b[n]\),我們需要\(o(n^2)\)的時間求出來,考慮怎麼樣求得更快
我們列舉乙個\(len\)表示我們現在想找到那些長度為\(2*len\)的\(\text\)串
然後在原串上每隔\(len\)放乙個斷點
我們列舉相鄰的兩個斷點\(i,j\),現在我們想要知道 以\(i\)開頭的字尾與以\(j\)開頭的字尾的最長公共字首(lcp) 和 以\(i\)結尾的字首與以\(j\)結尾的字首的最長公共字尾(lcs)
lcp可以用字尾陣列求;lcs也可以,把原陣列翻轉之後就變成字尾的lcp了,所以這兩個都是可以用st表\(o(1)\)求出的
那麼現在我們求出了這兩個值
情況1
對於這種情況,即\(lcp+lcs-1,我們是找不出\(\text\)串的
情況2
用腳畫圖 不愧是我
\(lcp+lcs-1,這個時候就有很多的長為\(2*len\)的\(\text\)串了,圖中畫出的\(\text,\ \text\)就是最靠左和最靠右的兩個這樣的串
實際上,我畫了"ok"的那個橙色區間的每乙個點都是乙個長為\(2*len\)的\(\text\)串的開頭,應該很好理解吧。。。
如何找哪一段是合法\(\text\)串的結尾也同理
所以實際上每次就是把\(a[n]\)和\(b[n]\)的某一段全部加一 用差分來維護一下就行了
最後來看一下時間複雜度
字尾陣列+st表是\(o(n\log n)\)
\(\frac+\frac+\frac+\dots+\frac\) 我記得差不多就是\(o(n \log n)\)吧。。。可能要稍微大一點
總之\(n\le 30000\)的資料是完全沒有壓力的
注意多組資料初始化陣列!注意多組資料初始化陣列!注意多組資料初始化陣列!
#include #define n 60005
using namespace std;
int t, n, nn;
char s[n];
int a[n], b[n];
int sa[n], sa2[n], rnk[n], sum[n], key[n], height[n], st[n][21];
inline bool check(int *num, int aa, int bb, int l)
void da()
} }void geth()
}void prest()
} }for (int i = 1; i <= nn; i++)
long long ans = 0;
for (int i = 1; i < nn; i++)
printf("%lld\n", ans);
} int main()
nn = n;
n = (n<<1|1);
da(); geth(); prest();
solve();
} 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設定乙個點,這樣乙個...