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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDU - 6661 Acesrc and String Theory (后缀数组)

發布時間:2024/4/18 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDU - 6661 Acesrc and String Theory (后缀数组) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接

題意

求字符串SSS中滿足子串可以復制KKK次得到的字串的數量,不位置字串相同的字符串分開計算

思路

看題解補完的這道題
枚舉循環節的長度XXX,從頭找到字符串SSS中連續出現循環節的最多的字串,假設當前求得的區間是[L,R][L, R][L,R]lcp(R+1,L)lcp(R+1, L)lcp(R+1,L)即為字串向后可以擴展的長度(長度小于XXX),相反求向前可以擴展的位置。這樣每次可以得到循環節為XXX的最長的連續區間,每次累計答案即可

#include <bits/stdc++.h> const int maxn = 1e6 + 5; using namespace std; struct SuffixArray{ // 下標int cntA[maxn], cntB[maxn], A[maxn], B[maxn];int Sa[maxn], tsa[maxn], height[maxn], Rank[maxn];int n, dp[maxn][21];void init(char *buf, int len) { // 預處理,sa,rank,heightn = len;for (int i = 0; i < 128; ++i) cntA[i] = 0;for (int i = 1; i <= n; ++i) cntA[(int)buf[i]]++;for (int i = 1; i < 128; ++i) cntA[i] += cntA[i-1];for (int i = n; i >= 1; --i) Sa[ cntA[(int)buf[i]]-- ] = i;Rank[ Sa[1] ] = 1;for (int i = 2; i <= n; ++i) {Rank[Sa[i]] = Rank[Sa[i-1]];if (buf[Sa[i]] != buf[Sa[i-1]]) Rank[Sa[i]]++;}for (int l = 1; Rank[Sa[n]] < n; l <<= 1) {for (int i = 0; i <= n; ++i) cntA[i] = 0;for (int i = 0; i <= n; ++i) cntB[i] = 0;for (int i = 1; i <= n; ++i) {cntA[ A[i] = Rank[i] ]++;cntB[ B[i] = (i + l <= n) ? Rank[i+l] : 0]++;}for (int i = 1; i <= n; ++i) cntB[i] += cntB[i-1];for (int i = n; i >= 1; --i) tsa[ cntB[B[i]]-- ] = i;for (int i = 1; i <= n; ++i) cntA[i] += cntA[i-1];for (int i = n; i >= 1; --i) Sa[ cntA[A[tsa[i]]]-- ] = tsa[i];Rank[ Sa[1] ] = 1;for (int i = 2; i <= n; ++i) {Rank[Sa[i]] = Rank[Sa[i-1]];if (A[Sa[i]] != A[Sa[i-1]] || B[Sa[i]] != B[Sa[i-1]]) Rank[Sa[i]]++;}}for (int i = 1, j = 0; i <= n; ++i) {if (j) --j;int tmp = Sa[Rank[i] - 1];while (i + j <= n && tmp + j <= n && buf[i+j] == buf[tmp+j]) ++j;height[Rank[i]] = j;}}void st() {for (int i = 1; i <= n; ++i) {dp[i][0] = height[i];}for (int j = 1; j <= log2(n); ++j) {for (int i = 1; i + (1 << j) - 1 <= n; ++i) {dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);}}}int rmq(int l, int r) {int len = r - l + 1;int x = log2(len);return min(dp[l][x], dp[r - (1 << x) + 1][x]);}int lcp(int x, int y) { // 最長公共前綴int l = Rank[x];int r = Rank[y];if (l > r) swap(l, r);return rmq(l+1, r);} }L, R; int work(int l, int r, int p, int len, int k) { // 計算前后綴,前后擴展,累計答案int rt = L.lcp(l, r+1); // 向右擴展int lt = R.lcp(len+1-r, len+1-l+1); // 向前擴展r += rt;l -= lt;return max(0, r-l+1 - k*p + 1); // 返回kp的個數 } char s[maxn], t[maxn]; int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int T;scanf("%d", &T);while (T--) {int k;long long ans = 0;scanf("%d%s", &k, t+1);int len = strlen(t+1);if (k == 1) {ans = 1ll * (1 + len) * len / 2;printf("%lld\n", ans);continue;}strcpy(s+1, t+1);reverse(t+1, t+1+len);s[0] = t[0] = '&';L.init(s, len); L.st();R.init(t, len); R.st();for (int i = 1; i <= len/2; ++i) { // 枚舉循環節int last = 1;for (int j = i+1; j <= len; j += i) { // 找到最長的循環節為i的字符串if (L.lcp(last, j) >= i) continue;ans += work(last, j-1, i, len, k);if (j+i-1 <= len) last = j;else last = 0;}if (last) ans += work(last, len, i, len, k);}printf("%lld\n", ans);}return 0; }

總結

以上是生活随笔為你收集整理的HDU - 6661 Acesrc and String Theory (后缀数组)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。