HDU6599 (字符串哈希+回文自动机)

题意:

求有多少个回文串的前len/2个字符也是回文串。(两组解可重复)
将这些回文串按长度分类,分别输出长度为1,2,...,n的合法串的数量。

 

题解:https://www.cnblogs.com/Cwolf9/p/11253106.html

我们使用回文自动机可以知道本质回文窜的个数与长度,我们在添加一个数组id[i], 记录第i个回文窜的结束位置就可以知道这个回文窜在原窜的区间[L,R];

我们在用字符串哈希就可以快速的判断前len/2个字符是不是回文了

,因为他本身是回文串,因此就是判断前后两部分是否相同

#include "bits/stdc++.h"
 
using namespace std;
const double eps = 1e-8;
#define reg register
#define lowbit(x) x&-x
#define pll pair<ll,ll>
#define pii pair<int,int>
#define fi first
#define se second
#define makp make_pair
 
int dcmp(double x) {
    if (fabs(x) < eps) return 0;
    return (x > 0) ? 1 : -1;
}
 
typedef long long ll;
typedef unsigned long long ull;
const ull hash1 = 201326611;
const ull hash2 = 50331653;
const int N = 300000 + 10;
const int M = 1000 + 10;
const int inf = 0x3f3f3f3f;
const ll mod = 998244353;
ll ret[N];
ull ha[N], pp[N];
 
ull getha(int l, int r) {
    if (l == 0) return ha[r];
    return ha[r] - ha[l - 1] * pp[r - l + 1];
}
 
bool check(int l, int r) {
    int len = r - l + 1;
    int mid = (l + r) >> 1;
    if (len & 1) return getha(l, mid) == getha(mid, r);
    else return getha(l, mid) == getha(mid + 1, r);
}
 
 
struct Palindromic_Tree {
    int nxt[N][30], fail[N], cnt[N];
    int num[N], len[N], s[N], id[N];
    int last, n, p;
 
    int newnode(int l) {
        memset(nxt[p], 0, sizeof(nxt[p]));
        cnt[p] = num[p] = 0;
        len[p] = l;
        return p++;
    }
 
    void init() {
        p = 0;
        newnode(0), newnode(-1);
        last = n = 0;
        s[0] = -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 (!nxt[cur][c]) {
            int now = newnode(len[cur] + 2);
            fail[now] = nxt[get_fail(fail[cur])][c];
            nxt[cur][c] = now;
            num[now] = num[fail[now]] + 1;
        }
        last = nxt[cur][c];
        cnt[last]++, id[last] = n;
    }
 
    ll Count() {
        for (int i = p - 1; i >= 0; i--) cnt[fail[i]] += cnt[i];
        for (int i = 2; i < p; i++) {
            ///cout << id[i] - len[i] << " " << id[i] - 1 << endl;
            if (check(id[i] - len[i], id[i] - 1)) {
                ret[len[i]] += cnt[i];
            }
        }
        return 0;
    }
} pam;
 
char str[N];
 
int main() {
    pp[0] = 1;
    for (int i = 1; i < N; i++) {
        pp[i] = hash1 * pp[i - 1];
    }
    while (~scanf("%s", str)) {
        memset(ret, 0, sizeof(ret));
        pam.init();
        int len = strlen(str);
        ha[0] = str[0];
        for (int i = 0; i < len; i++) {
            pam.add(str[i]);
        }
        for (int i = 1; i < len; i++) {
            ha[i] = ha[i - 1] * hash1 + str[i];
        }
        pam.Count();
        printf("%lld", ret[1]);
        for (int i = 2; i <= len; i++) {
            printf(" %lld", ret[i]);
        }
        printf("\n");
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/shuaihui520/p/11435833.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值