当前位置:首页 > 技术 > 正文内容

Manacher算法详解与应用实战

访客 技术 2026年8月14日 1

Manacher算法核心思想

Manacher算法是一种在线性时间复杂度O(n)内求解字符串最大回文子串长度的高效算法。其核心在于通过在原字符串中插入特殊分隔符,将所有回文问题统一转化为奇数长度回文的处理,并利用已知回文的对称性来加速计算。

构造扩展字符串

将原字符串每个字符之间插入一个特殊符号(如'#'),并在首尾添加边界标记(如'$'),形成新串。例如:

原串: "abc"
新串: "$#a#b#c#"

这样可确保所有回文中心均为奇数下标,且避免了偶数长度回文的复杂判断。

算法实现逻辑

维护两个关键变量:right 表示当前已处理范围内最远延伸位置,id 为该回文中心下标。对于每个位置 i,若其位于已有回文范围内,则可利用对称性快速初始化半径;否则从0开始扩展。

代码实现(以HDU 3068为例)

const int MAXN = 110005;
char original[MAXN], expanded[MAXN * 2];
int radius[MAXN * 2];

int solve() {
    char line[MAXN];
    while (scanf("%s", line) == 1) {
        int len = strlen(line);
        // 构造扩展串
        expanded[0] = '$';
        expanded[1] = '#';
        for (int i = 0; i < len; ++i) {
            expanded[i * 2 + 2] = line[i];
            expanded[i * 2 + 3] = '#';
        }
        int n = len * 2 + 2;
        expanded[n] = '\0';

        int rightmost = 0, center = 0;
        radius[0] = 0;

        for (int i = 1; i < n; ++i) {
            if (i < rightmost) {
                radius[i] = std::min(radius[center * 2 - i], rightmost - i);
            } else {
                radius[i] = 0;
            }

            // 向外扩展回文
            while (expanded[i + radius[i] + 1] == expanded[i - radius[i] - 1]) {
                ++radius[i];
            }

            // 更新最远扩展位置和中心
            if (i + radius[i] > rightmost) {
                rightmost = i + radius[i];
                center = i;
            }
        }

        int maxLen = 0;
        for (int i = 0; i < n; ++i) {
            maxLen = std::max(maxLen, radius[i]);
        }

        printf("%d\n", maxLen);
    }
    return 0;
}

进阶应用:HDU 3294 的字符变换处理

该题不仅要求找出最长回文子串,还需将其转换为特定字母映射规则下的输出结果。关键是根据回文中心与半径还原原始位置,并进行循环移位编码。

int main() {
    char input[200005];
    while (gets(input)) {
        int len = strlen(input);
        // 构建扩展串
        expanded[0] = '$';
        expanded[1] = '#';
        for (int i = 2; i < len; ++i) {
            expanded[i * 2 - 2] = input[i];
            expanded[i * 2 - 1] = '#';
        }
        int n = len * 2 - 2;
        expanded[n] = '\0';

        int rightmost = 0, center = 0;
        radius[0] = 0;

        for (int i = 1; i < n; ++i) {
            if (i < rightmost) {
                radius[i] = std::min(radius[center * 2 - i], rightmost - i);
            } else {
                radius[i] = 0;
            }

            while (expanded[i + radius[i] + 1] == expanded[i - radius[i] - 1]) {
                ++radius[i];
            }

            if (i + radius[i] > rightmost) {
                rightmost = i + radius[i];
                center = i;
            }
        }

        int maxRad = 0, midIdx = 0;
        for (int i = 1; i < n; ++i) {
            if (radius[i] > maxRad) {
                maxRad = radius[i];
                midIdx = i;
            }
        }

        if (maxRad <= 1) {
            puts("No solution!");
            continue;
        }

        int start = (midIdx - maxRad + 1) / 2 + 1;
        printf("%d %d\n", start - 2, start - 2 + maxRad - 1);

        for (int i = 0; i < maxRad; ++i) {
            char c = input[start + i];
            c = c - 'a' + 'A';  // 转大写
            c = (c - 'A' + 13) % 26 + 'A';  // ROT13 加密
            printf("%c", c);
        }
        puts("");
    }
    return 0;
}

相关文章

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

PHPStan 有什么用?怎么用?

PHPStan 是一个 PHP 的静态分析工具,在不运行代码的情况下就能帮你发现潜在问题,比如:传错类型(把 string 传给接受 int 的函数)访问不存在的属性 / 方法null 没处理好永远不会执行到的代码数组 key/值类型不一致返回值不符合声明注释和真实类型不匹配它非常适合:想提升代码质量、减少线上 bug、统一团队风格的人(尤其是中大型项目)。一、PHPStan 有什么用(通俗点说)...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。