CF1827C Palindrome Partition
前言
一个下午和 jimmywang 大力发明未果,写题解以记之。
思路
首先考虑求出以每个位置结尾的不可分割的偶回文串,这样就可以从前往后转移,但如果同一个位置结尾的这样的串有很多,
但其实每个点结尾的不可分割的偶回文串只有一个。
证明:假设以
这个位置结尾存在第二短的不可分割的偶回文串。记第一短的不可分割的偶回文串为 ,第二短的为 , 的左端点为 , 的对称中心的左侧点为 。若 ,那么 形如 ,其中 也为偶回文串,与 是不可分割的矛盾。若 ,则 形如 ,其中 与 都是偶回文串,与 是不可分割的矛盾。故 不存在,以 结尾的不可分割的偶回文串只有一个,且是以 结尾的最短的偶回文串。
可以结合图理解一下:
那么每个点只可能由前面的一个点转移过来,
只需要在合适的复杂度里找出以每个点为结尾的最短偶回文串即可。
众所周知,所以一个叫做车拉马的算法诞生了
先把所有对称中心以及每个中心当前对应期望回文串长度放进一个
非常的对啊。
被轻松
既然线性做不到,舍弃一点复杂度,多个 。
把所有右端点扔进一个
与之前不同的是,如果一个对称中心从队中取出,其原本期望长度对应的串是否为回文串用
但写完以后又挂了,对,又是它。答案是
一个普通队列倒下了,就有千万个优先队列站起来
按期望的回文串长度丢进优先队列(小根堆),复杂度
真的吗?这个过程唯一的问题在于这一步:
否则当前右端点没被标记过就标记,并找到下一个右端点继续入队
如果一个中心每次出队没有标记任何右端点又被入队,相当于它被虚空入队很多次,是否会使复杂度不是
暂时还不太会证,但是能过,只能感性理解所有中心一轮出入队必然能标记一些右端点,出队时期望右端点已被标记的情况数不会很多。
因为复杂度证不出来,jimmywang 觉得没有前途,改造了原版
后来发现 jimmywang 的方法在题解区有用链表实现
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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步