Maybe something w|

Semorius

园龄:1年9个月粉丝:4关注:10

2023-07-19 19:32阅读: 22评论: 0推荐: 0

CF1827C Palindrome Partition

CF1827C Palindrome Partition

前言

一个下午和 jimmywang 大力发明未果,写题解以记之。

思路

首先考虑求出以每个位置结尾的不可分割的偶回文串,这样就可以从前往后转移,但如果同一个位置结尾的这样的串有很多,dp 的复杂度肯定炸。

但其实每个点结尾的不可分割的偶回文串只有一个。

证明:假设以 x 这个位置结尾存在第二短的不可分割的偶回文串。记第一短的不可分割的偶回文串为 A,第二短的为 B, A 的左端点为 iB 的对称中心的左侧点为 j。若 j<i,那么 B 形如 A+C+A,其中 C 也为偶回文串,与 B 是不可分割的矛盾。若 ji,则 A 形如 D+E+D,其中 DE 都是偶回文串,与 A 是不可分割的矛盾。故 B 不存在,以 x 结尾的不可分割的偶回文串只有一个,且是以 x 结尾的最短的偶回文串。

可以结合图理解一下:

那么每个点只可能由前面的一个点转移过来,dp 的复杂度是 O(n) 的。

只需要在合适的复杂度里找出以每个点为结尾的最短偶回文串即可。

众所周知,Manachar 算法可以在线性时间内求解每个回文中心往外扩展的最长回文串。但我们要求的是最短。所以一个叫做车拉马的算法诞生了

先把所有对称中心以及每个中心当前对应期望回文串长度放进一个 pair 里入队,依然考虑从每个对称中心往外扩,如果当前期望长度下是回文串且右端点没被标记过,那就标记右端点,并把期望长度加 2 继续入队,否则直接出队。由于每个右端点只能被标记一次,复杂度 O(n)

非常的对啊

被轻松 Hack 了,例如在 abbbba 中,中间三个 bb 被标记完后就扩不到整个串了。

既然线性做不到,舍弃一点复杂度,多个 log 也没事嘛

把所有右端点扔进一个 set 里,表示未被标记的右端点的集合,每次入队前用 upper_bound 找到后面第一个没被标记右端点。

与之前不同的是,如果一个对称中心从队中取出,其原本期望长度对应的串是否为回文串用 Hash 判断。若不是则直接出队,否则当前右端点没被标记过就标记,并找到下一个右端点继续入队。

但写完以后又挂了,Hackabbbbaabbbba对,又是它。答案是 24,但输出是 23。原因是一个中心往下跳很远找到下一个右端点,并且赶在离那个右端点较近的中心之前把它标记了,就gg了。(上面这个串中最后一个 a 匹配了第一个 a

一个普通队列倒下了,就有千万个优先队列站起来

按期望的回文串长度丢进优先队列(小根堆),复杂度 O(nlogn)

真的吗?这个过程唯一的问题在于这一步:

否则当前右端点没被标记过就标记,并找到下一个右端点继续入队

如果一个中心每次出队没有标记任何右端点又被入队,相当于它被虚空入队很多次,是否会使复杂度不是 O(nlogn) 呢?

暂时还不太会证,但是能过,只能感性理解所有中心一轮出入队必然能标记一些右端点,出队时期望右端点已被标记的情况数不会很多。

因为复杂度证不出来,jimmywang 觉得没有前途,改造了原版 Manachar,严格 O(nlogn)

后来发现 jimmywang 的方法在题解区有用链表实现 O(n) 的。

Code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mpr make_pair
const ll SIZE = 500005;
const ll mod = 180181327;
ll T, n;
char ch[SIZE];
ll las[SIZE];
ll dp[SIZE];
ll sum[SIZE], sum1[SIZE];
ll pp[SIZE];
inline ll rd(){
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x*f;
}
ll power(ll x, ll y){
ll jl = 1;
while(y){
if(y & 1) jl = (jl * x) % mod;
x = (x * x) % mod;
y >>= 1;
}
return jl;
}
bool check(ll l, ll r){
if(l < 1 || r > n) return false;
if(l % 2 == r % 2) return false;
ll mid = (l + r) / 2;
ll x = (sum[mid] - ((sum[l-1] * pp[mid-l+1]) % mod) + mod) % mod;
ll y = (sum1[mid+1] - ((sum1[r+1] * pp[mid-l+1]) % mod) + mod) % mod;
return (x==y);
}
int main(){
T = rd(); priority_queue<pair<ll, ll> > q; set<ll> s;
pp[0] = 1;
for(ll i = 1; i <= 500000; i++) pp[i] = (pp[i-1] * 26) % mod;
while(T--){
n = rd(); cin >> ch+1;
sum[0] = sum1[n+1] = 0;
s.clear();
for(ll i = 1; i <= n; i++){
sum[i] = ((sum[i-1] * 26) % mod + ch[i] - 'a') % mod;
}
for(ll i = n; i >= 1; i--){
sum1[i] = ((sum1[i+1] * 26) % mod + ch[i] - 'a') % mod;
}
for(ll i = 1; i <= n; i++) las[i] = -1, s.insert(i);
for(ll i = 1; i <= n; i++) q.push(mpr(-1, i));
while(q.size()){
ll x = q.top().second, len = -q.top().first; q.pop();
if(x-len+1 < 1 || x+len > n) continue;
if(ch[x-len+1] != ch[x+len]) continue;
if(las[x+len] == -1){
las[x+len] = x-len;
s.erase(x+len);
}
if(s.upper_bound(x) != s.end()){
ll jl = *s.upper_bound(x);
if(check(x-(jl-x)+1, jl)) q.push(mpr(-(jl-x), x));
}
}
ll ans = 0;
for(ll i = 1; i <= n; i++){
if(las[i] != -1){
dp[i] = dp[las[i]] + 1, ans += dp[i];
}
else dp[i] = 0;
}
printf("%lld\n", ans);
}
return 0;
}

本文作者:Semorius

本文链接:https://www.cnblogs.com/Semorius/p/17566544.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Semorius  阅读(22)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起