生活随笔
收集整理的這篇文章主要介紹了
2019牛客暑期多校训练营(第四场)I - String (后缀自动机+回文树)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題目鏈接
題意
給一個(gè)字符串,求字符串子串的最大集合,集合中字符串互不相等,相等的定義為: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),對(duì)于這個(gè)新字符串可以用Sam求出不包含#\##的子串的種類數(shù)
- S=?rev(S)S =\not rev(S)S=??rev(S)的子串就會(huì)計(jì)算兩次
- S=rev(S)S = rev(S)S=rev(S)的子串就會(huì)計(jì)算一次,這個(gè)剛好是回文串
所以Sam計(jì)算的種類數(shù)加上所有的回文數(shù)就是ans×2ans × 2ans×2
如何計(jì)算不包含#\##的子串?
- 所有子串 - 包括#\##的字串 (len+1)2(len + 1)^2(len+1)2
- 對(duì)Sam進(jìn)行拓?fù)渑判?#xff0c;按照有效邊進(jìn)行轉(zhuǎn)移
#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
;while (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
]) {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'); }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
]; int len
[maxn
], S
[maxn
]; 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
) { while (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() { for (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;
}
總結(jié)
以上是生活随笔為你收集整理的2019牛客暑期多校训练营(第四场)I - String (后缀自动机+回文树)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。