看到回文串,于是就想到了马拉车。

马拉车可以帮我们求出每个 \(i\) 的最大扩展距离,容易得出,双回文串就是两个回文串拼一起。当然,两个回文串必须要相交,不然形不成一个字符串。

有的小可爱就会想直接找最大的两个扩展距离然后比一比,但是这是不行的。

因为两个回文串的相交部分必须砍成两个刚好相交的字符串,两个回文串也会因此而缩小。例如如下例子:

aaabaaabaaa

看起来两个回文串扩展距离都有 3 ,但中间是重叠的。最终分出的字符串只能是 aabaaaba ,其中 aabaaaba 是两个回文串。

如果我们按照上面的规则不难发现:两个中心点中间的一段就是实际上回文串的扩展距离,所以只要把中间距离 \(*2\) 就行。

这也告诉我们一件事情,就是在所有满足条件的中心点里面,离当前中心点最远的中心点最优。

不妨将每个点i只向后比较,那么我们要的就是在 log 的时间内快速找到最长回文串左端点在 i-hw[i] 左边(hw[i]为以i为中心的最大扩展距离,就是马拉车求的那个东西)的最大点。

一开始的想法是在平衡树上二分,但是带了两个log。后来想到可以用权值线段树解决。下标为每个点为中心点的最长回文串的左端点,权值为中心点。从最后的字符开始枚举,每次在1~hw[i]中找最大值。之后将 i 的左端点加入线段树。

由于添加了分隔符,所以最终答案要除2。发现前面有个乘2,干脆消掉好了。

但是这还不够。思考一件事情,如果新添加的左端点比原先的任意一个左端点还要大,那么肯定永远不会选到。如果我们把这些点删去且保持下标不变,就会得到一个权值单调上升的序列。每一个点的最优点为序列中下标小于该点右端点的最大值。

这样子可以得到一个常数很小的二分。但是还不够。

我们可以先把最终得到的单调上升的序列跑出来,用类似于并查集的手法处理一下,然后正着跑。这时序列中的点会不断减少(因为我们是倒着统计的),每减少一个点,就参照并查集的写法将两个区间合并。尽管这种做法对应的并查集只能进行路径压缩,但由于不能卡,并且开始可以用线性时间处理完第一波路径压缩,所以时间复杂度接近 \(O(nα(n))\) 按道理来说会比上面两种做法跑的都快,而且常数还很小。

好家伙,别人还在写带字符集大小复杂度的回文自动机呢,你的时间复杂度已经出现了一个逼格满满的α,这不显得很高级?为想出了一种并没有什么用的做法找借口

但我们有线性做法,我们处理出每一个字符串最长回文串的左端点和右端点,之后再跑一遍马拉车,但是这一次我们倒着跑。根据我们在二分做法中得出的结论,我们可以在每一个点进行一个一个更新时寻找那些没被标记过的右端点,如果左端点小于这些右端点则标记。如果有值呢?有值我们不需要管。因为这意味着一个距离更远的点能满足条件。时间复杂度和马拉车复杂度相同,为线性。

对于整个串为回文串的情况要单独处理,因为必须要能分成两个回文串。

注意一件事情,string在本地ubuntu跑的时候啥问题没有,但是在洛谷上对其中单个字符直接赋值就会变成精致小垃圾,私吞你的分数,还留下一两个AC让你不明所以。所以直接赋值最好用char。

这里只有权值线段树的做法

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = 1e5 + 5;
int hw[maxn << 1], n, b[maxn << 1];
char s[maxn << 2];
void change(string S)
{
s[0] = s[1] = '$';
for(int i = 0;i < n;i ++)
{
s[i * 2 + 2] = S[i];
s[i * 2 + 3] = '$';
}
n = n * 2 + 2;
s[n] = 0;
return ;
}
void maracher()
{
int maxr = 0,mid = 114514;
for(int i = 1;i < n;i++)
{
if(i < maxr) hw[i] = min(hw[(mid << 1) - i],hw[mid] + mid - i);
else hw[i] = 1;
for(;s[i + hw[i]] == s[i - hw[i]];++ hw[i]) ;
if(hw[i] + i > maxr)
{
maxr = hw[i] + i;
mid = i;
}
}
return ;
}
bool ton[maxn << 1];
struct linetree
{
int tr[maxn << 3];
#define ls(u) u<<1
#define rs(u) (u<<1)+1
void push_up(int u){ tr[u] = max(tr[ls(u)], tr[rs(u)]); }
void change(int p, int l, int r, int fl, int k)
{
if(l > fl || r < fl) return ;
if(l == r)
{
tr[p] = k;
return ;
}
int mid = l + r >> 1;
change(ls(p), l, mid, fl, k);
change(rs(p), mid + 1, r, fl, k);
push_up(p);
return ;
}
int query(int p, int l, int r, int fl, int fr)
{
if(l >= fl && r <= fr)
{
return tr[p];
}
if(l > fr || r < fl) return 0;
int mid = l + r >> 1, ans = 0;
ans = max(ans, query(ls(p), l, mid, fl, fr));
ans = max(ans, query(rs(p), mid + 1, r, fl, fr));
return ans;
}
}T;
char S[maxn];
bool hui()
{
int n = strlen(S);
for(int i = 0;i < n - i - 1;i ++) if(S[i] != S[n - i - 1]) return 0;
return 1;
}
signed main()
{
scanf("%s", S);
n = strlen(S);
int p = n;
change(S);
maracher();
int ans = 0;
for(int i = n - 1;i > 0;i --)
{
hw[i] --;
int j = T.query(1, 1, n, 1, i + hw[i]);
if(j != 0) ans = max(ans, (j - i));
if(ton[i - hw[i]] == 0)
{
ton[i - hw[i]] = 1;
T.change(1, 1, n, i - hw[i], i);
}
}
if(ans == p && hui()) cout << ans - 1 << endl;
else cout << ans << endl;
return 0;
}

P4555 最长双回文串 解题报告的更多相关文章

  1. 洛谷 P4555 [国家集训队]最长双回文串 解题报告

    P4555 [国家集训队]最长双回文串 题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为\(n\)的串 ...

  2. P4555 最长双回文串

    题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为 n的串 S ,求 S的最长双回文子串 T ,即可将 ...

  3. P4555 [国家集训队]最长双回文串

    P4555 [国家集训队]最长双回文串 manacher 用manacher在处理时顺便把以某点开头/结尾的最长回文串的长度也处理掉. 然后枚举. #include<iostream> # ...

  4. Manacher || P4555 [国家集训队]最长双回文串 || BZOJ 2565: 最长双回文串

    题面:P4555 [国家集训队]最长双回文串 题解:就.就考察马拉车的理解 在原始马拉车的基础上多维护个P[i].Q[i]数组,分别表示以i结尾最长回文子串的长度和以i开头的最长回文子串的长度 然后就 ...

  5. 【洛谷】P4555 [国家集训队]最长双回文串

    P4555 [国家集训队]最长双回文串 题源:https://www.luogu.com.cn/problem/P4555 原理:Manacher 还真比KMP好理解 解决最长回文串问题 转化为长度为 ...

  6. BZOJ 2565: 最长双回文串 [Manacher]

    2565: 最长双回文串 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1842  Solved: 935[Submit][Status][Discu ...

  7. 【BZOJ2565】最长双回文串(回文树)

    [BZOJ2565]最长双回文串(回文树) 题面 BZOJ 题解 枚举断点\(i\) 显然的,我们要求的就是以\(i\)结尾的最长回文后缀的长度 再加上以\(i+1\)开头的最长回文前缀的长度 至于最 ...

  8. BZOJ.2565.[国家集训队]最长双回文串(Manacher/回文树)

    BZOJ 洛谷 求给定串的最长双回文串. \(n\leq10^5\). Manacher: 记\(R_i\)表示以\(i\)位置为结尾的最长回文串长度,\(L_i\)表示以\(i\)开头的最长回文串长 ...

  9. Tsinsen 最长双回文串

    求最长双回文串,正反建回文树求最大. 题目链接:http://www.tsinsen.com/ViewGProblem.page?gpid=A1280 By:大奕哥 #include<bits/ ...

  10. bzoj 2565: 最长双回文串 manacher算法

    2565: 最长双回文串 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem. ...

随机推荐

  1. 20192305 王梓全Python程序设计实验一报告

    20192305 王梓全Python程序设计实验一报告 课程:<Python程序设计> 班级: 1923 姓名: 王梓全 学号:20192305 实验教师:王志强 实验日期:2021年4月 ...

  2. Ubuntu16.04配置网卡

    设置步骤: 1.路由器插电后,电脑使用网线,连接无线路由器任一LAN口,注意两者单独连接,先不要连接宽带网线.打开电脑浏览器,在地址栏输入192.168.100.1. 在路由器的管理界面,输入路由器的 ...

  3. 解决:pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it‘s not in your PATH. See README file for more information.

    问题:使用pytesseract库识别图片中文字时出现报错 代码: import pytesseract from PIL import Image,ImageEnhance img=Image.op ...

  4. 随机生成不重复的几个数(Unity)

    using System.Collections.Generic; using UnityEngine; /// <summary> /// 随机数管理类 /// </summary ...

  5. 利用expect交互批量修改普通用户过期密码

    利用expect交互批量修改普通用户过期密码 因为公司接了一个密码托管的活,有10000多台机器,系统有 openSUSE,SUSE,Ubuntu,centos,Oracle-Linux, 而且什么版 ...

  6. 操作系统|02.Linux基础(1)

    Linux基础 1.Linux系统安装.密码的破解 1.1常见的系统 unix:性能稳定,价格高昂,命令与Linux相通.多为大型政府单位.大型企业.金融机构使用. Linux:开源.自由 Linux ...

  7. JRebel4.2 使用之前的激活地址失效,需更改新的激活地址

    使用 https://jrebel.qekang.com 会报错 把 https://jrebel.qekang.com 换成 http://idea.javatiku.cn/ ,再进行激活,就ok了

  8. Vuex----Mutations

    注意: 只能通过 mutations里的函数才能修改 state 中的数据 第一种方法: const store = new Vuex.Store({ state:{ count:0 }, mutat ...

  9. centos7 安装 elasticsearch 7.15

    下载 elasticsearch wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.15.2-linu ...

  10. api接口基础Day1

    精华笔记: String: String的常用方法: length():获取字符串的长度(字符个数) trim():去除当前字符串两边的空白字符 toUpperCase()/toLowerCase() ...