I - I Love Palindrome String 19多校(回文自动机+hash)

本文介绍了一种使用回文自动机解决特定回文串问题的方法,即计算字符串中所有满足特定条件的回文子串数量。通过构建回文树并结合hash技巧,文章详细阐述了如何高效地找出字符串中不同长度的回文子串,特别是那些其一半也构成回文的子串。

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

You are given a string S=s1s2..s|S| containing only lowercase English letters. For each integer i∈[1,|S|] , please output how many substrings slsl+1...sr satisfy the following conditions:

∙ r−l+1 equals to i.

∙ The substring slsl+1...sr is a palindrome string.

∙ slsl+1...s⌊(l+r)/2⌋ is a palindrome string too.

|S| denotes the length of string S.

A palindrome string is a sequence of characters which reads the same backward as forward, such as madam or racecar or abba

.

Input

There are multiple test cases.

Each case starts with a line containing a string S(1≤|S|≤3×105)

containing only lowercase English letters.

It is guaranteed that the sum of |S| in all test cases is no larger than 4×106

.

Output

For each test case, output one line containing |S|

integers. Any two adjacent integers are separated by a space.

Sample Input

abababa

Sample Output

7 0 0 0 3 0 0

题目大意:对于每一个i(i的范围是1~字符串长度),能找到多少个本质不同的回文串并且满足这个回文串的一半也是回文串(若l~r为回文串,则l~(l + r)/ 2也为回文串)。

解题思路:我们需要知道所有的本质不同的回文串,所以回文自动机完全可以搞定。但是需要多加一个index数组。index[i] 是i号结点是插入到第 index[i] 号字符串产生的结点。作用是可以让我们找到一个回文串的左右两端的区间。若字符从0开始,index[i]对应 区间[ index[i] - len[i], index[i] - 1 ]。判断一半是否也是回文串,不需要真的就按照正常思维去判断。只需要判断l~r的回文串的前一半和后一半是否相同即可。为什么可以这样?恰恰就是因为l~r这个区间是回文串。写几个回文串就能明白了。

/*
@Author: Top_Spirit
@Language: C++
*/
#include <bits/stdc++.h>
using namespace std ;
typedef unsigned long long ull ;
typedef long long ll ;
const int Maxn = 3e5 + 10 ;
const int INF = 0x3f3f3f3f ;
const double PI = acos(-1.0) ;
const int seed = 133 ;

int n ;
int ans[Maxn] ;
ull pp[Maxn], Hash[Maxn] ;

struct palindromic_tree{
    int Next[Maxn][26] ;
    int fail[Maxn] ;
    int cnt[Maxn] ;
    int num[Maxn] ;
    int len[Maxn] ;
    int s[Maxn] ;
    int index[Maxn] ;
    int last ;
    int n, p ;
    int newNode (int k){
        for (int i = 0; i < 26; i++) Next[p][i] = 0 ;
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = k ;
        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 ;
            num[Now] = num[fail[Now]] + 1 ;
        }
        last = Next[cur][c] ;
        cnt[last]++ ;
        index[last] = n ;
    }
    ull getHash(int l, int r){
        if (l == 0) return Hash[r] ;
        else return Hash[r] - Hash[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 getHash(l, mid) == getHash(mid, r) ;
        else return getHash(l, mid) == getHash(mid + 1, r) ;
    }
    void Count(){
        for (int i = p - 1; i >= 0; i--){
            cnt[fail[i]] += cnt[i] ;
        }
        for (int i = 2; i < p; i++){
            if (check(index[i] - len[i], index[i] - 1)) {
                ans[len[i]] += cnt[i] ;
            }
        }

    }
}Tree;

char str[Maxn] ;

int main (){
    pp[0] = 1 ;
    for (int i = 1; i < Maxn; i++){
        pp[i] = pp[i - 1] * seed ;
    }
    while (cin >> str){
        int len = strlen(str) ;
        memset(ans, 0, sizeof(ans)) ;
        Tree.init() ;
        for (int i = 0; i < len; i++) Tree.add(str[i]) ;
        Hash[0] = str[0] ;
        for (int i = 1; i < len; i++){
            Hash[i] = Hash[i - 1] * seed + str[i] ;
        }
        Tree.Count() ;
        for (int i = 1; i <= len; i++){
            if (i == 1) cout << ans[i] ;
            else cout << " " << ans[i] ;
        }
        cout << endl ;
    }
    return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值