惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
S
Securelist
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
W
WeLiveSecurity
P
Palo Alto Networks Blog
O
OpenAI News
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
L
Lohrmann on Cybersecurity
NISL@THU
NISL@THU
T
Tor Project blog
T
Threatpost
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Latest news
Latest news
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
量子位
L
LINUX DO - 最新话题
Webroot Blog
Webroot Blog
F
Fortinet All Blogs
C
CERT Recently Published Vulnerability Notes
Hacker News: Ask HN
Hacker News: Ask HN
U
Unit 42
T
Tailwind CSS Blog
WordPress大学
WordPress大学
N
News | PayPal Newsroom
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
Netflix TechBlog - Medium
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Stack Overflow Blog
Stack Overflow Blog
Cloudbric
Cloudbric
C
Check Point Blog
V
Visual Studio Blog
The GitHub Blog
The GitHub Blog
The Hacker News
The Hacker News
B
Blog

hsfzxjy 的博客

解决 VSCode + CMake + MSVC 编译器信息乱码的问题 使用 3090 部署 1.58bit 动态量化版 DeepSeek R1 671b 如何在 VS Code DevContainer 中配置 HTTP 代理 如何在跳板机背后的服务器上使用 VS Code Remote - Containers Cohesive Digests for Ints and Floats Rust 中的隐匿概念 —— Place(位置) 美术馆 一尺之槌,日取其半,1075日而竭 老生常谈:使用 Cloudflare 自选 IP 加速站点访问 辩义 State、Nation 与 Country 将 Base64 编码的数据快速转换为 Uint8Array 折腾 NPU·第1章 —— 搭建 Level Zero 开发环境 折腾 NPU·第0章 —— Intel NPU 概述与 Level-Zero 新增域名 monad.run CSS 中为特定字符设置不同字体 Arbitary Lifetime Transmutation via Rust Unsoundness Dijkstra 算法的延伸 硬卧 Go Fact: Zero-sized Field at the Rear of a Struct Has Non-zero Size Display *big.Rat Losslessly and Smartly in Golang 代码的仪式 Building Electron From Scratch 中式亲属称谓研究之一:构建半群 Some Notes on Kotlin Coroutines Git sparse-checkout and partial clones for Mega-Repos 辩义“封建” Diving from the CUDA Error 804 into a bug of libnvidia-container Modern Cryptography, GPG and Integration with Git(hub) Move the Root Partition of Ubuntu A New Programmer Kicks a Roadblock Git-based Dependencies in Dart and Go Reversy Naming 人类一败涂地 Invalid Golang Pointers Can Bite You Even If You Don't Dereference Side Project(副业) A Flaw of Promoting Complex Trait Bounds in Rust Initialize Process Pool Worker with Individual Value Rust - Python FFI From Scratch [Extending Hexo For My Site] Part 1 [Extending Hexo For My Site] Part 0 Debug a 'torch.tensor(1).cuda()' hanging 不自由的互联网 Retrieve Contents over HTTP without curl or wget [Unravelling mocona] Part 1 - Verbosity or Anti-Pattern [Unravelling mocona] Part 0 - Preface Understanding pickle in Python Rough Notes on Deploying Vaultwarden & NextCloud Bookmarks 语言狂热者与实用主义者 Demystify the randomness in CUDA kernels Performant Bulk Mutations in IndexedDB Auto Rebuild .pyx Files with pyximport Cython and Threads Obtain a Random Available TCP Port with Bash Information Theory: KL Divergence Information Theory: Entropy and Mutual Information 铁板烧 西郊线 Proof of the Gumbel Max Trick Option::as_ref Rc, RefCell and Interior Mutability Visualizing Correlation 三月十日杂感 三月一日杂感 二月十一日杂感 一月二十六日杂感 SS Configuration 一月七日杂感 四月·病 Haskell 笔记:State Monad Haskell 笔记:Monad 引论 Haskell 笔记:Applicative Haskell 笔记:Category Theory and Functor Haskell 笔记:data, type, newtype Haskell 笔记:folds 使用 Aria2 在 Ubuntu 中下载百度云资源 从伪并行的 Python 多线程说起 一个 Reentrant Error 引发的对 Python 信号机制的探索和思考 Linux 文件权限 HSFZMUN 4.0 部署小记 午后雨·科大 最后的雨夜·广州 揭秘·变态的平方根倒数算法 神坑·Python 装饰类无限递归 Python“黑魔法”之 Encoding & Decoding Ubuntu 重新映射键盘布局 为什么我要翻墙 Python“黑魔法”之 Generator Coroutines 数学美 之 判断线段相交的最简方法 除夕杂感 17 行代码实现的简易 Javascript 字符串模板 Python“黑魔法”之 Meta Classes 诗集 生活,需要被“发现” 家书·十八岁成人礼 炫技?还是需求? 【译】响应式图片的现状 【译】“为什么有这么多的编程语言?” Wisecity 商赛总结——也谈前端自动化测试 记一次 DoS 诈骗网站的经历 那一年,我们望向星空
Manacher 回文计数算法
2024-01-16 · via hsfzxjy 的博客

以下假设字符串下标从 $0$ 开始,子串记号 $s[i..j]$ 左闭右闭。

给定长度为 $n$ 的字符串 $s$,Manacher 算法可以在 $O(n)$ 的时间复杂度内找到 $s$ 的所有回文子串。

我们先以寻找长度为奇数的子串为例。首先需要明确的是,如果 $s$ 中以第 $i$ 个字符为中心的最长回文子串长度为 $d=2p+1$,则以下皆为 $s$ 的回文子串:

$$s[i-(p-1)..i+(p-1)],\ldots, s[i-1..i+1], s[i..i]$$

因此,我们只需对所有下标 $i$ 求解出以 $s[i]$ 为中心的最长回文子串长度 $2p_i+1$,即可知道 $s$ 的所有回文子串。

Manacher 算法从左到右求解诸 $p_i$。在求解 $p_i$ 时,算法会保证诸 $p_0,\ldots,p_{i-1}$已经求解完毕,从而我们可以利用先前的信息合理减少计算。在这个过程中,我们将维护一个滚动的子串 $t=s[l..r]$,$t$ 是目前已探明的右端点最靠右的回文子串。如此一来,每次循环时,我们要考虑两种情况:

  1. i 在子串 t 内,即 $l\leq i \leq r$。此时我们考虑 $i$ 在 $t$ 内的镜像点 $j = l+r-i$,则 $p_j$ 是之前已经求解过的。从而知道以 $j$ 为中心的最长子串长度为 $p_j$。但这一知识不能照搬到 $i$ 处,因为 $i+p_j$ 很可能会超过右端点 $r$。为此我们做一个截断,取 $p_i = min(p_j,r-i)$;
  2. i 不在子串 t 内,即 $i > r$。此时我们取 $p_i=0$。

以上得到的 $p_i$ 其实还不是真正的解,因为 $i + p_i$ 的右侧还有未探明的情况。因此我们还需循环自增 $p_i$,直到 $s[i-p_i]\neq s[i+p_i]$。注意到随着 $p_i$ 的扩进,$s[i-p_i..i+p_i]$ 成为了新的端点最右的子串,因此我们还要及时更新 $t$。用 Python 实现的算法如下:

def manacher_odd(s):
n = len(s)
s = '^' + s + '$'
p = [0] * n
i = l = 1
for i in range(1, n + 1):
x = max(0, min(r - i, p[l + r - i]))
while s[i + x] == s[i - x]: x += 1
p[i - 1] = x - 1
if i + x > r:
l, r = i - x, i + x
return p

为什么 Manacher 算法复杂度是 $O(n)$ 呢?我们可以从 $r$ 端点的变化考虑。在每次循环中,$r$ 有两种情形:

  1. r 不变,此时 $p_i$ 是直接从其镜像点 $p_j$ 拿到的值,从而第二层的 while 循环没有运行;
  2. r 增加,此时第二层的 while 循环执行了,将 $p_i$ 从 $r_\text{old}-i$ 增加到了 $r_\text{new} -i$。

由此可见,第二层 while 循环的总次数等于 $r$ 从始至终的增量,为 $n$。因此整个算法的时间复杂度为 $O(n)$。

同样地,我们还能计数 $s$ 中长度为偶数的回文子串。以下为 Python 代码,原理类似:

def manacher_even(s):




n = len(s)
s = '^' + s + '$'
p = [0] * (n - 1)
l = r = 1
for i in range(1, n):
j = max(0, min(r - i - 1, p[l + r - i - 3]))
while s[i + j + 1] == s[i - j]:
j += 1
p[i - 1] = j
if i + j + 2 > r:
l, r = i - j + 1, i + j + 1
return p

作者:hsfzxjy
链接:
许可:CC BY-NC-ND 4.0.
著作权归作者所有。本文不允许被用作商业用途,非商业转载请注明出处。