日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【NOI2016】优秀的拆分(后缀数组)

發(fā)布時間:2025/7/14 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【NOI2016】优秀的拆分(后缀数组) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

題目描述

如果一個字符串可以被拆分為AABBAABB的形式,其中 A和 B是任意非空字符串,則我們稱該字符串的這種拆分是優(yōu)秀的。

例如,對于字符串aabaabaa,如果令?A=aabB=a,我們就找到了這個字符串拆分成?AABB的一種方式。

一個字符串可能沒有優(yōu)秀的拆分,也可能存在不止一種優(yōu)秀的拆分。比如我們令?A=aB=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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。