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

马拉车可以帮我们求出每个 \(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. 【Java】生成随机字符串

    package com.runsky.utils; import java.util.Random; public class GetRandom { private static final Str ...

  2. Coursera Programming Languages, Part C 华盛顿大学 Week 1

    来进入这一系列课程的最后一 Part 啦! \(P1\) 我们介绍了 \(ML\),一种 static typing 的 functional language \(P2\) 我们介绍了 \(Rack ...

  3. 「SOL」旧试题 (LOJ/SDOI)

    数论+图论,妙不可言 # 题面 给定 \(A,B,C\),求: \[\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C\sigma_0(ijk) \] 数据规模:\(A,B,C\ ...

  4. AD9834配置

    SPI设置 注意,虽然手册上CPOL(CLK空闲时电平)为低,但实测是是高电平 连接 SYNC 为片选引脚,使用时拉低 FSE/PSE为频率与相位寄存器选择,均拉低即可 void SPI_Write_ ...

  5. openEuler22.09初始化脚本

    #!/bin/bash s=`nmcli d | grep "已断开" | awk '{print $1}'` echo /etc/sysconfig/network-script ...

  6. 通过modbus+socket+GPRS采集硬件数据

    # !/usr/bin/python # -*- coding: UTF-8 -*- import sys import os TESTCASE = os.path.dirname(os.path.a ...

  7. jetbrain 全套激活

    关于 jetbrain 专业版激活的教程很多,发现很多实际操作不太友好,本人亲测可激活经理 1.下载 ja-ja-netfilter-all https://github.com/byebai95/j ...

  8. Git基础使用和在UE中使用的方法

    Git使用介绍 Git使用 1.基础知识 pwd 显示目前的工作目录 print work directory ls 显示当前路径下所有文件 mkdir 产生新的文件夹make directory t ...

  9. C语言II—作业03

    1.作业头 这个作业属于哪个课程 https://edu.cnblogs.com/campus/zswxy/SE2020-3 这个作业要求在哪里 https://edu.cnblogs.com/cam ...

  10. jsp第10个作业

    package Servlet; import JDBC.JDBC; import javax.servlet.ServletException; import javax.servlet.annot ...