最长回文子串的Manacher算法

 manacher算法(民间称马拉车算法233)是用来找字符串中的最长回文子串的,先来说一下什么是回文串,像这样“abcba”这样一个字符串找到一个中间位置,然后分别向他的左边和右边相等的距离位置的字符是相同的,那么这个字符串就称为回文串,“abcba”这个字符串的len为5是奇数,我们可以找到一个中间字符,然后进行搜索也可以找出来(当然时间复杂度是比较高的),但是当我们遇到一个长度为偶数的字符串时该怎么找中间字符呢,像这样“abccba”,下面我们引入Manacher算法,这是一个可以将长度为奇数或偶数的字符串一起考虑的神奇算法

    Manacher算法可以将长度为奇数和偶数的回文串一起考虑:在原字符串的相邻字符串之间插入一个分隔符,字符串的首尾也要分别添加,注意分隔符必须是原字符串中没有出现过的

原字符串sababc
转换后字符串str#a#b#a#b#c#

 

一、Len数组的简单介绍

    Manacher算法中用到一个非常重要的辅助数组Len[i]表示以str[i]为中心的最长回文子串的最右端到str[i]位置的长度,比如以str[i]为中心的最长回文串是str[l,r],那么Len[i]=r-i+1

转换后的字符串str#a#b#a#b#c#
Len12141412121

 

    Len[i]数组有一个性质,Len[i]-1就等于该回文串在原串s中的长度

    证明:在转换后的字符串str中,所有的回文串的长度都是奇数,那么对于以str[i]为中心的最长回文串的长度为2*Len[i]-1,其中又有Len[i]个分隔符,所以在原字符串中的长度就是Len[i]-1,那么剩下的工作就是求Len数组

二、Len数组的计算

    从左往右开始计算,假设0<=j<=i,那么在计算Len[i]时,Len[j]已经计算过了,设mx为之前计算过的最长回文串的右端点,id为取得这个端点值得位置(那么Len[id]=mx-id+1)

第一种情况:i<=mx.

    找到i相对于id的对称位置,设为j,再次分为两种情况:

        1、Len[j]<mx-i

        

       mx的对称点为2*id-mx,i和j所包含的范围是2*Len[j]-1

        那么说明以j为中心的回文串一定在以id为中心的回文串内部,且i和j关于id对称,由回文串的定义可知,一个回文串反过来仍是回文串,所以以i为中心的回文串长度至少和以i为中心的回文串长度相等,即Len[i]>=Len[j].因为Len[j]<mx-i所以i+Len[j]<mx,由对称性可知Len[i]=Len[j].

        2、Len[j]>=mx-i

        由对称性说明以i为中心的回文串可能延伸到mx之外,而大于mx的部分我们还没有进行匹配,所以要从mx+1位置开始一个一个匹配直到失配,从而更新mx和对应的id以及Len[i]

第二种情况,i>mx

        如果i比mx还大,说明对于中点为i的回文串一点都没匹配,这个时候只能一个个匹配(滑稽),匹配完成后更新mx的位置和对应的id及Len[i].

代码实现:

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1e6+5;
char s[maxn*2],str[maxn*2];
int Len[maxn*2],len;

void getstr()
{
    int k=0;
    str[k++]='$';
    for(int i=0;i<len;i++)
        str[k++]='#',
        str[k++]=s[i];
    str[k++]='#';
    len=k;
}
void Manacher()
{
    getstr();
    int mx=0,id;
    for(int i=1;i<len;i++)
    {
        if(mx>i) Len[i]=min(Len[2*id-i],mx-i);
        else Len[i]=1;
        while(str[i+Len[i]]==str[i-Len[i]]) 
            Len[i]++;
        if(Len[i]+i>mx)
            mx=Len[i]+i,id=i;
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&s);
        len=strlen(s);
        Manacher();
        int ans=1;
        for(int i=1;i<len;i++) ans=max(ans,Len[i]);
        printf("%d\n",ans-1);
    }
    return 0;
}

中心扩展法:

#include <iostream>
#include <string>
using namespace std;
int LongestPalindrome(string &str){
    if(str.size() == 0)
        return 0;
    int i, j, maxLen=1;
    for(i = 0; i < str.size(); ++i){
        //该点为奇数字符数的回文串中心
        for(j = 0; i-j >= 0 && i+j <= str.size()-1; ++j){
            if(str[i-j] != str[i+j])
                break;
        }
        if(maxLen < 2*(j-1) + 1) //注意跳出的j的取值,如asdsf,j=2时跳出,而最长回文子串为:2*(j-1)
            maxLen = 2*(j-1) + 1;
        //该点为偶数数字符数的回文串中心
        for(j = 0; i-j>=0 && i+j+1 <= str.size()-1; ++j){
            if(str[i-j] != str[i+j+1])
                break;
        }
        if(maxLen < 2*(j-1) + 2)
            maxLen = 2*(j-1) + 2;
    }
    return maxLen;          
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XXX项目 软件需求规格说明书 版本:V1.1 文 档 编 号 保 密 等 级 作 者 最后修改日期 审 核 人 最后审批日期 批 准 人 最后批准日期 修订记录 日期 版本 修订说明 修订人 目 录 1 简介 2 1.1 背景 2 1.2 目的 2 1.3 范围 2 1.4 缩略语 2 1.5 参考文档 2 2 运行环境 2 2.1 硬件环境 3 2.2 网络环境 3 2.3 软件环境 3 3 系统结构 3 3.1 系统功能结构 3 3.2 本系统与其它系统的关系 3 3.3 系统的行为架构 3 4 软件系统功能需求 3 对于结构化分析请采用如下格式: 4 4.1 XXX软件需求 4 4.1.1 需求编号 4 4.1.2 复杂度 4 4.1.3 优先级 4 4.1.4 功能描述 4 4.1.5 输入 4 4.1.6 处理 4 4.1.7 输出 4 4.1.8 约束条件 4 4.1.9 相关的性能需求 4 对于UML分析请采用如下格式: 4 4.2 XXX用例-UC001 4 5 非功能性需求 7 5.1 性能需求 7 5.2 可靠性需求 7 5.3 可移植性需求 7 5.4 可维护性需求 7 5.5 可用性需求 7 5.6 兼容性需求 7 5.7 分布性需求 7 6 在线用户文档和帮助系统需求 7 7 设计约束 7 8 需要购买的组件 7 9 接口与协议 7 9.1 用户接口 7 9.2 硬件接口 7 9.3 软件接口 7 9.4 通信接口 8 10 词汇表 8 简介 背景 【说明项目背景】 目的 【说明编写SRS的目的】 范围 【说明该SRS在整个项目周期的适用范围】 术语/缩略语 【说明本SRS涉及的术语、缩略语的详细含义】 参考文档 【说明编写SRS时引用和参考的文档】 运行环境 【说明用户期望的待开发系统的运行环境】 硬件环境 网络环境 软件环境 系统结构 【说明软件系统的业务层次结构,以及与其他外部系统之间的业务接口关系。】 系统功能结构 【说明软件系统的分解结构,可以采用功能分解树,或者系统静态架构图,或者高层系统用例模型图表示】 本系统与其它系统的关系 【对于研发项目和预研项目,说明该软件产品在ModelBank中的逻辑位置;对于客户化子项目,说明该软件系统在大系统中的逻辑位置,可以采用逻辑结构图表示】 系统的行为架构 【如果采用UML开发,可以说明系统的高层业务行为模型】 软件系统功能需求 【说明待开发系统业务功能需求被分配的软件需求,并统一编号组织。】 Eg:SRS1 XXX SRS1.1 XXX 或者 UC1 XXX UC1.1XXX XXX软件需求(对于结构化分析请采用如下格式) 需求编号 功能描述 输入 处理 输出 约束条件 相关的性能需求 XXX用例-UC001(对于UML分析请采用如下格式) 用例功能描述:制订修改计划的申请信息,并上报给上级单位。用户可以查询、查看计划调整申请信息。 界面图: 申请计划调整―――查看计划调整申请界面 申请计划调整―――计划调整界面 界面说明: 查询条件描述 序号 栏位名称 栏位说明 备注 页面表示形式 1 计划文号 字符型 64位长 输入项 弹出页面显示 2 申请标题 字符型 128位长 输入项 输入框 3 申请日期 日期型,YYYY-MM-DD;分为开始日期和截止日期 输入项 日期控件 计划调整申请信息描述 序号 栏位名称 栏位说明 备注 页面表现形式 1 标题 输入 必输项 输入框 2 调整顺序号 输入,字符型 32位长 输入项 文本输入框 3 调整原因 用户输入调整的原因和建议调整的其它说明信息 必输项 文本输入框 4 负责人 库内负责轮换申请人的姓名,20字符。 必输项 下拉列表框 5 申请日期 计划调整申请提出的具体日期,系统自动读取服务器日期。 输入项 日期控件 6 调整后开始日期 本库计划调整后的开始时间 必输项 日期控件 7 调整后结束日期 本库计划调整后的结束时间 必输项 日期控件 8 原计划文号 输入,字符型20位长 显示项 文本 9 原计划标题 输入,字符型128位长 显示项 文本 10 计划开始执行日期 原计划中的开始执行日期 显示项 文本 11 计划截止执行日期 原计划中的截止执行日期 显示项 文本 前置条件:无 后置条件:无 功能说明: 查询功能:查询已经制订的计划调整申请信息。查询条件:计划文号,申请标题,申请日期。查询列表:申请标题、计划文号,申请日期、负责人、状态。系统默认按申请日期降序排序。 查看计划调整申请信息功能:查看一条具体的计划调整申请信息,页面上并能显示原计划信息。 根据计划调整申请,查看原计划信息; 新增计划调整申请信息:用户首先选择原计划信息,然后对原计划进行修改; 修改计划调整申请信息:用户可以看到原计划信息; 删除计划调整申请信息; 打印计划调整申请信息; 上报功能:将此调整申请上报给上级单位。 特殊说明: 计划调整申请只对上级下发计划有用; 只有未上报的计划调整申请才能修改和删除; 计划调整申请的状态分为:上报,未上报; 已上报的计划调整申请的状态以红色表示; 在新增一个计划调整申请时,用户选择了计划文号后,系统取出此计划的信息并按仓号显示在页面上; 此模块的上报仅仅是将审核状态置为”已审核”,具体的对上级单位的提交由交换模块完成 业务实体描述 业务实体描述是根据需求分析过程中抽象出来的业务实体、数据需求以及业务实体之间的关系的描述。描述的内容包括需要画出数据概念模型图、实体字段信息、实体数据的来源、业务基础数据等内容。具体参考如下: 【1. 静态数据需求 列举描述在系统中,作为基础数据使用的基本不变的数据需求。例如:“员工档案”等数据的需求。 2. 动态数据需求 列举描述本软件所处理的动态数据需求。例如:销售数据、交易数据等等 3. 业务实体描述 描述本软件选用的业务实体、实体字段描述、字段类型描述、实体具体的数据库容量预测、命名标识等 4. 数据词典 列举描述本软件的数据字典。数据字典覆盖静态用户数据和动态用户数据两方面。 5. 数据采集需求 列举本软件的数据采集需求。包括数据采集的方式(自动采集或者交互式人工采集)、数据采集的频度说明、精度要求说明等等。 6. 数据精度需求 根据具体情况,描述本软件中的数据精确度需求。包括计算的精度(用小数位数表示)、有效数据位数等等方面的需求。】 非功能性需求 性能需求 【如果需要,说明用户提出的,或者应该满足的对软件系统性能的要求,并统一编号组织。比如:系统吞吐量,平均交易响应时间,平均无故障时间等】 可靠性需求 可移植性需求 可维护性需求 可用性需求 兼容性需求 分布性需求 故障处理需求 【描述对于各种主要故障的处理需求。包括处理方式、提示信息内容与格式和善后处理算法等等】 在线用户文档和帮助系统需求 【如果需要,说明在线文档和帮助系统的要求】 设计约束 【说明实现本系统必须服从的设计决策,比如:开发平台,语言,体系结构,采用的类库】 需要购买的组件 【如果需要,说明需要购买的软件组件】 接口与协议 用户接口 【说明软件要实现的用户接口】 硬件接口 【如果需要,说明软件必须支持的硬件接口】 软件接口 【说明系统外部的软件接口关系】 通信接口 【如果需要,说明系统与其他外部的软件通信协议】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值