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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

2019牛客暑期多校训练营(第四场)I - String (后缀自动机+回文树)

發布時間:2024/4/18 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2019牛客暑期多校训练营(第四场)I - String (后缀自动机+回文树) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接

題意

給一個字符串,求字符串子串的最大集合,集合中字符串互不相等,相等的定義為:a=?ba =\not ba=??b 而且 a=?rev(b)a =\not rev(b)a=??rev(b)
例如字符串abacabacabac,最大不相等集合 {abac,b,a,ab,aba,bac,ac,c}\{ abac,b,a,ab,aba,bac,ac,c\}{abac,b,a,ab,aba,bac,ac,c}

思路

我們可以將字符串SSS改為S+#+rev(S)S + \# + rev(S)S+#+rev(S),對于這個新字符串可以用Sam求出不包含#\##的子串的種類數

  • S=?rev(S)S =\not rev(S)S=??rev(S)的子串就會計算兩次
  • S=rev(S)S = rev(S)S=rev(S)的子串就會計算一次,這個剛好是回文串

所以Sam計算的種類數加上所有的回文數就是ans×2ans × 2ans×2

如何計算不包含#\##的子串?
  • 所有子串 - 包括#\##的字串 (len+1)2(len + 1)^2(len+1)2
  • 對Sam進行拓撲排序,按照有效邊進行轉移
#include <bits/stdc++.h> const int maxn = 4e5 + 5; using namespace std; struct SAM{int trans[maxn<<1][27], slink[maxn<<1], maxlen[maxn<<1];int indegree[maxn<<1], endpos[maxn<<1], rank[maxn<<1];int last, now, root, len;inline void newnode (int v) {maxlen[++now] = v;}inline void extend(int c) {newnode(maxlen[last] + 1);int p = last, np = now;// 更新transwhile (p && !trans[p][c]) {trans[p][c] = np;p = slink[p];}if (!p) slink[np] = root;else {int q = trans[p][c];if (maxlen[p] + 1 != maxlen[q]) {// 將q點拆出nq,使得maxlen[p] + 1 == maxlen[q]newnode(maxlen[p] + 1);int nq = now;memcpy(trans[nq], trans[q], sizeof(trans[q]));slink[nq] = slink[q];slink[q] = slink[np] = nq;while (p && trans[p][c] == q) {trans[p][c] = nq;p = slink[p];} }else slink[np] = q;}last = np;// 初始狀態為可接受狀態endpos[np] = 1;}inline void build(char *s) {root = last = now = 1;for (int i = 0; s[i]; ++i) extend(s[i] - 'a'); // extend(s[i] - '1');}inline long long getNum() {long long ans = 0;for (int i = 2; i <= now; ++i) {ans += maxlen[i] - maxlen[slink[i]];}return ans;} }sam; struct Palindrome_Tree{int nex[maxn][26];int fail[maxn], cnt[maxn], num[maxn]; // num 記錄每個節點右端點的表示回文串的個數int len[maxn], S[maxn]; // cnt 記錄每個節點表示的回文串出現的次數int last, n, p;int newnode(int l) { // 新建節點for (int i = 0; i < 26; ++i) nex[p][i] = 0;cnt[p] = num[p] = 0;len[p] = l;return p++;}void init() { // 初始化p = 0;newnode(0), newnode(-1); // 新建奇根和偶根last = n = 0;S[n] = -1; fail[0] = 1; // 偶根指向}int get_fail(int x) { // 求failwhile (S[n - len[x] - 1] != S[n]) x = fail[x];return x;}void add(int c) { // 添加節點c -= 'a';S[++n] = c;int cur = get_fail(last);if (!nex[cur][c]) {int now = newnode(len[cur] + 2);fail[now] = nex[get_fail(fail[cur])][c];nex[cur][c] = now;num[now] = num[fail[now]] + 1;}last = nex[cur][c];cnt[last]++;}void count() { // 求cntfor (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];} }Tree; char s[maxn]; int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);scanf("%s", s);Tree.init();int len = strlen(s);s[len] = 26 + 'a';for (int i = 0; i < len; ++i) {Tree.add(s[i]);s[i+len+1] = s[len-i-1];}s[len+len+1] = 0;sam.build(s);long long ans = (sam.getNum() - 1ll * (len+1) * (len+1) + Tree.p - 2) / 2;printf("%lld\n", ans);return 0; }

總結

以上是生活随笔為你收集整理的2019牛客暑期多校训练营(第四场)I - String (后缀自动机+回文树)的全部內容,希望文章能夠幫你解決所遇到的問題。

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