I - I Love Palindrome String more than 19 (palindrome automaton +hash)

Posted by AlexP on Thu, 03 Oct 2019 06:02:51 +0200

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

Topic: For each I (i ranges from 1 to string length), how many palindrome strings can be found and half of the palindrome strings are palindrome strings (if l~r is palindrome strings, l~(l+r)/2 is palindrome strings).

Solution: we need to know all the palindrome strings of different nature, so palindrome automata can be done. But you need to add an index array. index[i] is the node that I is inserted into the index[i] string. The function is to find an interval between the left and right ends of a palindrome string. If the character starts at 0, index[i] corresponds to intervals [index[i] - len[i], index[i] - 1]. Judging whether half of them are palindrome strings does not really need to be judged according to normal thinking. It is only necessary to judge whether the first half of the palindrome string of l~r is the same as the second half of the palindrome string. Why is that possible? It is precisely because the interval l~r is a palindrome string. Write a few palindrome strings and you will understand.

/*
@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 ;
}