【NOI2016】优秀的拆分(后缀数组)
題目描述
如果一個字符串可以被拆分為AABBAABB的形式,其中 A和 B是任意非空字符串,則我們稱該字符串的這種拆分是優(yōu)秀的。
例如,對于字符串aabaabaa,如果令?A=aab,B=a,我們就找到了這個字符串拆分成?AABB的一種方式。
一個字符串可能沒有優(yōu)秀的拆分,也可能存在不止一種優(yōu)秀的拆分。比如我們令?A=a,B=baa,也可以用?AABB表示出上述字符串;但是,字符串?abaabaa?就沒有優(yōu)秀的拆分。
現(xiàn)在給出一個長度為?n的字符串S,我們需要求出,在它所有子串的所有拆分方式中,優(yōu)秀拆分的總個數(shù)。這里的子串是指字符串中連續(xù)的一段。
以下事項需要注意:
出現(xiàn)在不同位置的相同子串,我們認為是不同的子串,它們的優(yōu)秀拆分均會被記入答案。
在一個拆分中,允許出現(xiàn)A=B。例如?cccc?存在拆分A=B=c。
字符串本身也是它的一個子串。
輸入格式
每個輸入文件包含多組數(shù)據(jù)。
輸入的第一行只有一個整數(shù)T,表示數(shù)據(jù)的組數(shù)。保證?1≤T≤10。
接下來?T行,每行包含一個僅由英文小寫字母構(gòu)成的字符串S,意義如題所述。
輸出格式
輸出?T行,每行包含一個整數(shù),表示字符串S?所有子串的所有拆分中,總共有多少個是優(yōu)秀的拆分。
輸入輸出樣例
輸入 #1 ? 4 aabbbb cccccc aabaabaabaa bbaabaababaaba 輸出 #1 ? 3 5 4 7【題目大意】
? 給定一個字符串,從里面查找一共有多少個字符串滿足AABB形式,輸出個數(shù)
【算法分析】
?考慮這個拼接的情況,AABB
?所以我們只需要找所有的AA/BB,然后左乘右獲得最終答案
?然后問題就轉(zhuǎn)化成了如何求AA和BB
?利用后綴數(shù)組我們能夠在nlogn的時間復(fù)雜度內(nèi)求取出LCP
?同時我們把字符串翻轉(zhuǎn),然后就能夠求到LCS
?而這兩個字符串如果重合,就說明能夠出現(xiàn)AA的這種形式的重復(fù)
定義:
?f [ i ]? 為以 i 開頭的符合條件的AA型字符串
g [ i ]? 為以 i 結(jié)尾的符合條件的AA型字符串
然后我們只需要把他們 相乘累加即可
注意:
最后一個不能夠重疊,所以最后一個位置( n )不符合要求
?
【代碼】
?
#include<cstdio> #include<cstring> #include<string> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 30000+ 5; int lg[MAXN], f[MAXN], g[MAXN];struct suff_string {int n,m;int x[MAXN] = { 0 }, y[MAXN] = { 0 }, c[MAXN] = { 0 };int SA[MAXN], rak[MAXN], height[MAXN];int st[17][MAXN];void build_SA(char *s){n = strlen(s + 1);m = 128;for (int i = 1; i <= 30000; i++)x[i] = y[i] = c[i] = 0;for (int i = 1; i <= n; i++)SA[i] = rak[i] = height[i] = 0;for (int i = 1; i <= n; i++)++c[x[i] = s[i]];//計算每一個字符的數(shù)量,同樣的放在一起for (int i = 2; i <= m; i++)c[i] += c[i - 1];//求前綴和for (int i = n; i >= 1; i--)SA[c[x[i]]--] = i;//從后往前,這樣就能求出最開始順序for (int k = 1; k <= n; k <<= 1){int num = 0;for (int i = n - k + 1; i <= n; i++) y[++num] = i;//把最后幾個字符放進去for (int i = 1; i <= n; i++)//按照順序遍歷if (SA[i] > k)//如果長度大于ky[++num] = SA[i] - k;//把第一個放進去for (int i = 1; i <= m; i++)c[i] = 0;for (int i = 1; i <= n; i++)c[x[i]]++;//x[i]是第一關(guān)鍵字for (int i = 2; i <= m; i++)c[i] += c[i - 1];//求前綴和for (int i = n; i >= 1; i--)SA[c[x[y[i]]]--] = y[i], y[i] = 0;swap(x, y);x[SA[1]] = 1; num = 1;for (int i = 2; i <= n; i++)x[SA[i]] = (y[SA[i]] == y[SA[i - 1]] && y[SA[i] + k] == y[SA[i - 1] + k]) ? num : ++num;if (num == n) break;m = num;}for (int i = 1; i <= n; i++)rak[SA[i]] = i;return;}void get_height(char *s){int k = 0;for (int i = 1; i <= n; i++) {if (k) k--;int j = SA[rak[i] - 1];while (i+k<=n && j+k<=n &&s[i + k] == s[j + k]) k++;height[rak[i]] = k;}}void build_ST(){memset(st, 0, sizeof(st));for (int i = 1; i <= n; i++)st[0][i] = height[i];//printf("1\n");for (int i = 1; i <= 16; i++)for (int j = 1; j <= n; j++)st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);//printf("2\n"); } int query(int l, int r){l = rak[l],r = rak[r];if (l > r) swap(l, r); l++;int t = lg[r + 1 - l];return min(st[t][l], st[t][r - (1 << t) + 1]);} }A,B; char s[MAXN]; void solve() {//printf("%s\n", s+1);//printf("**\n");scanf("%s", s + 1);A.build_SA(s); A.get_height(s); A.build_ST(); int N = A.n;reverse(s + 1, s + 1 + N);//printf("%s\n", s + 1); B.build_SA(s); B.get_height(s); B.build_ST();//printf("%d %d \n",A.n,B.n);for (int i = 1; i <=N; i++)f[i] = 0, g[i] = 0;for (int len = 1; len <= N / 2; len++){for (int i = len, j = i + len; j <= N; i += len, j += len){// printf("**\n");int lcp = min(A.query(i, j), len), lcs = min(B.query(N - i + 2, N - j + 2), len - 1);// printf("**\n");int t = lcp + lcs - len + 1;if (lcp + lcs >= len){g[i - lcs]++, g[i - lcs + t]--;f[j + lcp - t]++, f[j + lcp]--;}}}//printf("**\n");for (int i = 1; i <= N; i++)f[i] += f[i - 1], g[i] += g[i - 1];long long ans = 0;for (int i = 1; i <N; i++) ans += 1ll * f[i] * g[i + 1];printf("%lld\n", ans);} int main() {int T;for (int i = 2; i <= 30000; i++) lg[i] = lg[i >> 1] + 1;scanf("%d", &T);while (T--){solve();}return 0; } View Code?
?
紀念我水過的第二道黑題
?
?
?
?
?
?
?
?
??
轉(zhuǎn)載于:https://www.cnblogs.com/rentu/p/11426187.html
總結(jié)
以上是生活随笔為你收集整理的【NOI2016】优秀的拆分(后缀数组)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ImportError: Failed
- 下一篇: 自己挖的坑自己填--JVM报内存溢出