poj3974 Palindrome 最长回文子串 (二分 + hash)2022

题目链接:https://ac.nowcoder.com/acm/contest/1008/C
题面:题面
思路:先对字符串进行正向哈希和反向哈希,然后枚举间断点
分两种情况讨论
1,回文序列为奇数时,枚举点为中间点
2,回文序列为偶数时,枚举点为中间间隔

在这里主要想讲一下二分
首先我们要先确定对什么进行二分,有两种思路
1)对回文串的起点进行二分(我们枚举的点为中点)
2)对回文串的一半长度进行二分
如果我们对回文串的起点进行二分,那么我们枚举点超过所给字符串的一半时,对二分的参数要进行多个改变,l, r都要改变;最后计算长度的公式也要改变;
所以我们选择对回文串一半的长度进行二分

然后我们确定如何二分,即二分的结果是什么?我们可以知道,二分的结果是在满足为回文序列的情况下,找到最大的回文长度。
所以在二分过程中,我们的边界的更新应该这样的

	int mid = (l + r + 1) >> 1; //find the most
	if(t1 == t2)    l = mid;
    else    r = mid - 1;

最后则是注意一些特殊情况,回文序列为偶数时,我们枚举的是中间间隔,所以二分区间也要变为

	r = min(i, len - i);
    t2 = calcu(i, 0, r, 1);   m2 = max(m2, t2);

还有就是h2[len + 1] = 0; 防止在更新h2中的点的哈希值时,受上一个case的影响
具体参考代码实现

AC代码

#include <iostream>
#include <cstdio>
#include <stdio.h>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <stdlib.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <deque>
#include <map>
#include <set>
#define Pi acos(-1.0)
#define Mid ll + ((rr - ll) >> 1)
#define Lson p << 1, ll, Mid
#define Rson p << 1 | 1, Mid + 1, rr
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int P = 131, N = 1e6 + 5;
char s[N];
ULL h1[N], h2[N], p[N];
int t, m1, m2, len;

int calcu(int id, int l, int r, bool f){
    while(l < r){
        int mid = (l + r + 1) >> 1; //find the most
        ULL t1, t2;
        if(!f){
            t1 = h1[id - 1] - h1[id - mid - 1] * p[mid];
            t2 = h2[id + 1] - h2[id + mid + 1] * p[mid];
        }
        else{
            t1 = h1[id] - h1[id - mid] * p[mid];
            t2 = h2[id + 1] - h2[id + mid + 1] * p[mid];
        }
        if(t1 == t2)    l = mid;
        else    r = mid - 1;
    }
    return f ? l * 2 : l * 2 + 1;
}

int main(){
    p[0] = 1;
    for(int i = 1; i < N; ++i){
        p[i] = p[i - 1] * P;
    }
    while(1){
        scanf("%s", s + 1);
        if(strcmp(s + 1, "END") == 0)   break;
        m1 = 0; m2 = 0;
        len = strlen(s + 1);
        h2[len + 1] = 0;
        for(int i = 1; i <= len; ++i){
            h1[i] = h1[i - 1] * P + (s[i] - 'a' + 1);
            h2[len - i + 1] = h2[len - i + 2] * P + (s[len - i + 1] - 'a' + 1);
        }
        for(int i = 1; i <= len; ++i){
            int t1, t2, r;
            r = min(i - 1, len - i);
            t1 = calcu(i, 0, r, 0);   m1 = max(m1, t1);
            r = min(i, len - i);
            t2 = calcu(i, 0, r, 1);   m2 = max(m2, t2);
            //printf("t1 : %d, t2 : %d, m1 : %d, m2 : %d\n", t1, t2, m1, m2);
        }
        printf("Case %d: %d\n", ++t, max(m1, m2));
    }

    //system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值