IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 什么是Manacher(马拉车)算法-java代码实现 -> 正文阅读

[Java知识库]什么是Manacher(马拉车)算法-java代码实现

截止到目前我已经写了 500多道算法题,其中部分已经整理成了pdf文档,目前总共有1000多页(并且还会不断的增加),大家可以免费下载
下载链接https://pan.baidu.com/s/1hjwK0ZeRxYGB8lIkbKuQgQ
提取码:6666

在这里插入图片描述
之前在讲《517,最长回文子串的3种解决方式》的时候,在最后提到过Manacher算法,但是没有写,这里单独拿出来写。
在这里插入图片描述
在这里插入图片描述
我们来看个例子,比如字符串"babad"在添加特殊字符之后每个字符的回文半径

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果还看不明白,我们来随便找个字符串 “babcbabcbac” 画个图来看下
在这里插入图片描述

在这里插入图片描述
代码如下,分三种情况判断

    for (int i = 0; i < length; i++) {
        if (i < maxRight) {
            //情况一,i没有超出范围[left,maxRight]
            //2 * maxCenter - i其实就是j的位置,实际上是判断p[j]<maxRight - i
            if (p[2 * maxCenter - i] < maxRight - i) {
                //j的回文半径没有超出范围[left,maxRight],直接让p[i]=p[j]即可
                p[i] = p[2 * maxCenter - i];
            } else {
                //情况二,j的回文半径已经超出了范围[left,maxRight],我们可以确定p[i]的最小值
                //是maxRight - i,至于到底有多大,后面还需要在计算
                p[i] = maxRight - i;
                //继续计算
                while (i - p[i] >= 0 && i + p[i] < length && res[i - p[i]] == res[i + p[i]])
                    p[i]++;
            }
        } else {
            //情况三,i超出了范围[left,maxRight],就没法利用之前的已知数据,而是要一个个判断了
            p[i] = 1;
            //继续计算
            while (i - p[i] >= 0 && i + p[i] < length && res[i - p[i]] == res[i + p[i]])
                p[i]++;
        }
    }

在来看下最终代码

public String longestPalindrome(String s) {
    int charLen = s.length();//源字符串的长度
    int length = charLen * 2 + 1;//添加特殊字符之后的长度
    char[] chars = s.toCharArray();//源字符串的字符数组
    char[] res = new char[length];//添加特殊字符的字符数组
    int index = 0;
    //添加特殊字符
    for (int i = 0; i < res.length; i++) {
        res[i] = (i % 2) == 0 ? '#' : chars[index++];
    }

    //新建p数组 ,p[i]表示以res[i]为中心的回文串半径
    int[] p = new int[length];
    //maxRight(某个回文串延伸到的最右边下标)
    //maxCenter(maxRight所属回文串中心下标),
    //resCenter(记录遍历过的最大回文串中心下标)
    //resLen(记录遍历过的最大回文半径)
    int maxRight = 0, maxCenter = 0, resCenter = 0, resLen = 0;
    //遍历字符数组res
    for (int i = 0; i < length; i++) {
        if (i < maxRight) {
            //情况一,i没有超出范围[left,maxRight]
            //2 * maxCenter - i其实就是j的位置,实际上是判断p[j]<maxRight - i
            if (p[2 * maxCenter - i] < maxRight - i) {
                //j的回文半径没有超出范围[left,maxRight],直接让p[i]=p[j]即可
                p[i] = p[2 * maxCenter - i];
            } else {
                //情况二,j的回文半径已经超出了范围[left,maxRight],我们可以确定p[i]的最小值
                //是maxRight - i,至于到底有多大,后面还需要在计算
                p[i] = maxRight - i;
                while (i - p[i] >= 0 && i + p[i] < length && res[i - p[i]] == res[i + p[i]])
                    p[i]++;
            }
        } else {
            //情况三,i超出了范围[left,maxRight],就没法利用之前的已知数据,而是要一个个判断了
            p[i] = 1;
            while (i - p[i] >= 0 && i + p[i] < length && res[i - p[i]] == res[i + p[i]])
                p[i]++;
        }
        //匹配完之后,如果右边界i + p[i]超过maxRight,那么就更新maxRight和maxCenter
        if (i + p[i] > maxRight) {
            maxRight = i + p[i];
            maxCenter = i;
        }
        //记录最长回文串的半径和中心位置
        if (p[i] > resLen) {
            resLen = p[i];
            resCenter = i;
        }
    }
    //计算最长回文串的长度和开始的位置
    resLen = resLen - 1;
    int start = (resCenter - resLen) >> 1;
    //截取最长回文子串
    return s.substring(start, start + resLen);
}

上面都通过画图分析很好理解,可能稍微有点不好理解的是后面3行代码,resLen就是最大回文半径,resCenter就是最大回文子串(添加特殊字符之后的)中间的那个字符。我们可以根据下面这个图可以看到,原字符串中回文串的长度就是添加特殊字符之后的回文半径-1

在这里插入图片描述
上面是分为3种情况来判断的,实际上我们还可以把上面3种情况合并

        //合并后的代码
        p[i] = maxRight > i ? Math.min(maxRight - i, p[2 * maxCenter - i]) : 1;
        //上面的语句只能确定i~maxRight的回文情况,至于maxRight之后的部分是否对称,
        //就只能一个个去匹配了,匹配的时候首先数组不能越界
        while (i - p[i] >= 0 && i + p[i] < length && res[i - p[i]] == res[i + p[i]])
            p[i]++;

我们来看下合并后的最终代码

// 返回最长回文串长度
public String longestPalindrome(String s) {
    int charLen = s.length();//源字符串的长度
    int length = charLen * 2 + 1;//添加特殊字符之后的长度
    char[] chars = s.toCharArray();//源字符串的字符数组
    char[] res = new char[length];//添加特殊字符的字符数组
    int index = 0;
    //添加特殊字符
    for (int i = 0; i < res.length; i++) {
        res[i] = (i % 2) == 0 ? '#' : chars[index++];
    }

    //新建p数组 ,p[i]表示以res[i]为中心的回文串半径
    int[] p = new int[length];
    //maxRight(某个回文串延伸到的最右边下标)
    //maxCenter(maxRight所属回文串中心下标),
    //resCenter(记录遍历过的最大回文串中心下标)
    //resLen(记录遍历过的最大回文半径)
    int maxRight = 0, maxCenter = 0, resCenter = 0, resLen = 0;
    //遍历字符数组res
    for (int i = 0; i < length; i++) {
        //合并后的代码
        p[i] = maxRight > i ? Math.min(maxRight - i, p[2 * maxCenter - i]) : 1;
        //上面的语句只能确定i~maxRight的回文情况,至于maxRight之后的部分是否对称,
        //就只能一个个去匹配了,匹配的时候首先数组不能越界
        while (i - p[i] >= 0 && i + p[i] < length && res[i - p[i]] == res[i + p[i]])
            p[i]++;
        //匹配完之后,如果右边界i + p[i]超过maxRight,那么就更新maxRight和maxCenter
        if (i + p[i] > maxRight) {
            maxRight = i + p[i];
            maxCenter = i;
        }
        //记录最长回文串的半径和中心位置
        if (p[i] > resLen) {
            resLen = p[i];
            resCenter = i;
        }
    }
    //计算最长回文串的长度和开始的位置
    resLen = resLen - 1;
    int start = (resCenter - resLen) >> 1;
    //截取最长回文子串
    return s.substring(start, start + resLen);
}

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:36:58  更:2021-07-30 12:37:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/29 1:35:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码