HDU 4426 Palindromic Substring(回文树+哈希)

本文介绍了如何利用回文树解决HDU 4426题目的方法,重点在于计算本质不同的回文串数量及其回文串前一半的起始位置,并通过哈希值进行排序和查找。在实现过程中要注意可能的整数溢出问题,可以使用long long类型来避免。此外,还提供了解题思路和代码实现,总结了处理回文串数量时需要注意的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4426


WA:

1.k有没有 long long

2.回文串是取前一半


解题思路:

利用回文树获取本质不同的回文串的数量以及他们回文串前一半第一次出现的位置(起点终点位置)。

我们搞一个结构体:记录上面的信息,同时还需要一个变量计算hash值

对于每一次询问,我们O(N)预处理整个串的hash以及26的幂次方(后者总共只需预处理一次),然后O(1)计算每个回文串的hash值,sort对结构体按照hash大小排个序。

排好序后,然后开一个数组维护排序后回文串数量前缀和,二分查找k,看k落在哪个前缀和里面,就取哪个前缀和下标对应结构体的hash值


代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define df(x) ll x;scanf("%I64d",&x)
#define df2(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define mod 777777777
#define duozu(T) int T;scanf("%d",&T);while (T--)

const int N = 1e5+5;

const int maxn = 1e5+5;
const int ALP = 26;

char s[N];
struct node
{
    int l,r;
    ll hsh;
    int cnt;
    bool operator < (const node& a)const{
        return hsh < a.hsh;
    }
}pr[maxn];
ll pre[maxn],hsh[maxn],mi[maxn];

void prework()
{
    mi[0] = 1;
    mi[1] = 26;
    for1(i,1,N-5) mi[i] = (mi[i-1]*26)%mod;
}

struct PAM{
    int next[maxn][ALP];
    int fail[maxn];
    int len[maxn];
    int s[maxn];
    int last;
    int cnt[maxn];
    int n;
    int p;

    int newnode(int w){
        for(int i=0;i<ALP;i++)
            next[p][i] = 0;
        len[p] = w;
        cnt[p] = 0;
        return p++;
    }
    void init(){
        p = 0;
        newnode(0);
        newnode(-1);
        last = 0;
        n = 0;
        s[n] = -1;
        fail[0] = 1;
    }
    int get_fail(int x){
        while(s[n-len[x]-1] != s[n]) x = fail[x];
        return x;
    }
    void add(int c){
        c -= 'a';
        s[++n] = c;
        int cur = get_fail(last);
        if(!next[cur][c]){
            int now = newnode(len[cur]+2);
            fail[now] = next[get_fail(fail[cur])][c];
            next[cur][c] = now;
            pr[now].l = n-len[now]+1;
            pr[now].r = pr[now].l + (len[now]+1)/2-1;
        }
        last = next[cur][c];
        cnt[last]++;
    }
    void count(){
        for(int i=p-1;i>=0;i--)
            cnt[fail[i]] += cnt[i];
    }
}pam;

int val[26];

int main()
{
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    int n,q;
    ll k;
    prework();
    duozu(T){
        scanf("%d %d",&n,&q);
        scanf("%s",s);
        pam.init();
        for0(i,0,n) pam.add(s[i]);
        pam.count();
        for0(i,2,pam.p) pr[i].cnt = pam.cnt[i];

        while (q--){
            scanf("%lld",&k);
            for0(i,0,26) scanf("%d",val+i);
            hsh[1] = val[s[0]-'a'];
            for0(i,1,n) hsh[i+1] = (hsh[i]*26 + val[s[i]-'a'])%mod;///哈希从1开始表示第几个到第几个

            for0(i,2,pam.p){
                int l = pr[i].l,r = pr[i].r;///第几个到第几个
                pr[i].hsh = (hsh[r] - hsh[l-1]*mi[r-l+1]%mod + mod)%mod;
                //printf("pr[i].hsh=%lld\n",pr[i].hsh);
            }

            sort(pr+2,pr+pam.p);

            for0(i,2,pam.p) pre[i] = pre[i-1] + pr[i].cnt;

            int pos = lower_bound(pre+2,pre+pam.p,k)-pre;

            printf("%lld\n",pr[pos].hsh);

        }
        puts("");
    }
    return 0;
}

总结:牵扯到回文串数量的相关变量记得谨慎思考会不会爆int,或者干脆longlong一了百了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值