字符串哈希-Acwing-回文子串的最大长度

字符串哈希-Acwing-回文子串的最大长度

题目:

如果一个字符串正着读和倒着读是一样的,则称它是回文的。

给定一个长度为N的字符串S,求他的最长回文子串的长度是多少。

输入格式
输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个小写字符的形式给出。

输入以一个以字符串“END”(不包括引号)开头的行表示输入终止。

输出格式
对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。

每个输出占一行。

输入样例:
abcbabcbabcba
abacacbaaaab
END
输出样例:
Case 1: 13
Case 2: 6

题意:

求 给 定 字 符 串 的 最 长 回 文 子 串 的 长 度 。 求给定字符串的最长回文子串的长度。

题解:

对 字 符 串 正 序 和 逆 序 分 别 哈 希 , 再 从 前 到 后 枚 举 回 文 子 串 的 中 心 , 二 分 回 文 半 径 , 输 出 最 大 的 回 文 半 径 。 对字符串正序和逆序分别哈希,再从前到后枚举回文子串的中心,二分回文半径,输出最大的回文半径。

细节方面:

  1. 由 于 最 大 长 度 可 能 时 奇 数 , 可 能 是 偶 数 , 为 了 优 化 代 码 复 杂 度 , 对 每 一 个 字 符 串 预 处 理 , 在 相 邻 两 个 字 符 之 间 插 入 一 个 相 同 的 其 他 字 符 , 将 长 度 为 S 的 字 符 串 转 化 为 S + S − 1 = 2 S − 1 的 长 度 为 奇 数 的 字 符 串 。 注 意 : 预 处 理 完 了 后 , 字 符 串 的 长 度 n 要 记 住 乘 2 , 防 止 遍 历 出 错 。 由于最大长度可能时奇数,可能是偶数,为了优化代码复杂度,对每一个字符串预处理,在相邻两个字符\\之间插入一个相同的其他字符,将长度为S的字符串转化为S+S-1=2S-1的长度为奇数的字符串。\\注意:预处理完了后,字符串的长度n要记住乘2,防止遍历出错。 SS+S1=2S1n2
  2. 求 正 序 和 逆 序 的 哈 希 表 h l 和 h r 时 , 指 针 i , j 从 1 和 n 分 别 开 始 递 增 和 递 减 。 求正序和逆序的哈希表hl和hr时,指针i,j从1和n分别开始递增和递减。 hlhri,j1n
  3. 二 分 枚 举 中 点 在 i 的 回 文 子 串 的 半 径 时 , 答 案 必 然 小 于 等 于 字 符 串 长 度 的 一 半 , 因 此 二 分 的 左 右 端 点 l = 0 , r = m i n ( i − 1 , n − i ) 。 此 外 , 在 求 取 中 点 右 半 部 分 也 就 是 回 文 子 串 的 后 半 部 分 时 , 区 间 端 点 要 翻 转 过 来 , 如 下 图 : 二分枚举中点在i的回文子串的半径时,答案必然小于等于字符串长度的一半,\\因此二分的左右端点l=0,r=min(i-1,n-i)。此外,在求取中点右半部分也就是\\回文子串的后半部分时,区间端点要翻转过来,如下图: il=0,r=min(i1,ni)
    在这里插入图片描述

子 串 前 半 部 分 的 哈 希 表 区 间 为 [ i − m i d , i − 1 ] , 后 半 部 分 的 区 间 为 [ n − i + 1 − m i d , n − i + 1 − 1 ] 。 子串前半部分的哈希表区间为[i-mid,i-1],后半部分的区间为[n-i+1-mid,n-i+1-1]。 [imid,i1][ni+1mid,ni+11]

如 果 二 者 哈 希 值 不 等 , 说 明 半 径 二 分 得 过 大 , 则 r = m i d − 1 。 如果二者哈希值不等,说明半径二分得过大,则r=mid-1。 r=mid1

  1. 最 终 求 答 案 时 , 还 要 考 虑 回 文 串 两 端 的 情 况 , ① 、 若 两 端 是 字 母 , 说 明 字 母 比 添 加 的 符 号 多 一 个 , 那 么 回 文 串 长 度 为 ⌈ 2 l + 1 2 ⌉ 。 ② 、 若 两 端 是 新 增 的 符 号 , 说 明 符 号 比 字 母 多 一 个 , 那 么 回 文 串 长 度 就 是 回 文 半 径 。 ③ 、 维 护 所 有 回 文 半 径 中 的 最 大 值 即 可 得 最 终 答 案 。 最终求答案时,还要考虑回文串两端的情况,\\①、若两端是字母,说明字母比添加的符号多一个,那么回文串长度为\lceil\frac{2l+1}{2}\rceil。\\②、若两端是新增的符号,说明符号比字母多一个,那么回文串长度就是回文半径。\\③、维护所有回文半径中的最大值即可得最终答案。 22l+1

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ull unsigned long long
using namespace std;

const int N=1e6+10;
const int base=131;

char str[2*N];
ull hl[2*N],hr[2*N],p[2*N];

ull get(ull h[],int l,int r)
{
    return h[r]-h[l-1]*p[r-l+1];
}

int main()
{
    int T=1;
    while(~scanf("%s",str+1),strcmp("END",str+1))
    {
        ///添加'{'符号
        int n=strlen(str+1);
        for(int i=2*n;i;i-=2)
        {
            str[i]=str[i/2];
            str[i-1]='z'+1;
        }
        ///长度增大
        n*=2;

        p[0]=1;
        for(int i=1,j=n;i<=n;i++,j--)
        {
            hl[i]=hl[i-1]*base+str[i]-'a'+1;
            hr[i]=hr[i-1]*base+str[j]-'a'+1;
            p[i]=p[i-1]*base;
        }

        int res=0;
        for(int i=1;i<=n;i++)
        {
            int l=0,r=min(i-1,n-i);
            while(l<r)
            {
                int mid=l+r+1>>1;
                ///hl左端点i-mid,右端点i-1;hr左端点要翻转
                if(get(hl,i-mid,i-1)!=get(hr,n-(i+mid)+1,n-(i+1)+1)) r=mid-1;
                else l=mid; ///mid=l+r+1>>1
            }

            ///字母多一个,回文半径是l,总长度为2l+1
            if(str[i-l]<='z') res=max(res,(2*l+1)/2+1);///上取整
            else res=max(res,l);
        }

        printf("Case %d: %d\n",T++,res);
    }

    return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值