I Love Palindrome String (Palindrome + String hash)

Keywords: PHP

http://acm.hdu.edu.cn/showproblem.php?pid=6599

Title Description:

Give you a string and find out how many palindrome strings are essentially different, and half of these palindrome strings are palindrome strings.

Ideas:

This question gives me a systematic understanding of palindrome tree operations and functions.

  • Find the number of essentially different palindrome strings in the prefix 0~i of string S (essentially different if two strings have different lengths or the same lengths and at least one character is different)
  • Find the number of occurrences of each essentially different palindrome string in string S
  • Find the number of palindrome strings in string S (actually combining 1 and 2)
  • Find the number of palindrome strings ending with the following index i

Here is a good blog, and my template is copied from here.

https://blog.csdn.net/u013368721/article/details/42100363#

Here is another blog about how to construct and build a tree to enhance understanding.

https://www.cnblogs.com/cjyyb/p/8460058.html

In this question, we not only need to know how many palindrome substrings are essentially different, but also need to determine whether they satisfy half of them are substrings.

So we add an id array to the palindrome tree to record the position + 1 at the end of each palindrome substring. The palindrome tree itself has a len[i] to indicate the length of the current node palindrome string. So we have the position and length of the palindrome string, and we can get the palindrome string. Then we use the string hash to judge whether a hash satisfies the first one. Just two conditions.

This code is saved as a template

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN =  300000 + 10;
const int N = 26 ;
int id[MAXN];//The location of each string
int ans[MAXN];//Number of occurrences per length

char s[MAXN];
const int hash1=201326611;
const int hash2=50331653;
const int mod=1e9+7;
int ha[MAXN],pp[MAXN];
//String hash
int get_hash(int l,int r){
    if(l==0)return ha[r];
    return (ha[r]-ha[l-1]*pp[r-l+1]%mod+mod)%mod;
}
//Judging whether this interval is palindrome
bool check(int l,int r){
    int len=r-l+1;
    int mid=(l+r)>>1;
    //Judge whether the front and back are equal
    if(len&1){
        return get_hash(l,mid)==get_hash(mid,r);
    }
    else{
        return get_hash(l,mid)==get_hash(mid+1,r);
    }
}
struct Palindromic_Tree {
	int next[MAXN][N] ;//next pointer, which is similar to dictionary tree, points to a string consisting of the same character at both ends of the current string.
	int fail[MAXN] ;//Fail pointer, jump to the node pointed by fail pointer after mismatch
	int cnt[MAXN] ; //Represents the number of essentially different strings represented by node i, and finally count s them
	int num[MAXN] ; //Number of palindrome strings that end at the right end of the longest palindrome string represented by node i
	int len[MAXN] ;//len[i] represents the length of palindrome string represented by node i
	int S[MAXN] ;//Store added characters
	int last ;//Point to the node where the previous character is located for the next add
	int n ;//Character Array Pointer
	int p ;//Node pointer

	int newnode ( int l ) {//New Node
		for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
		cnt[p] = 0 ;
		num[p] = 0 ;
		len[p] = l ;
		return p ++ ;
	}

	void init () {//Initialization
		p = 0 ;
		//Two root nodes
		newnode (  0 ) ;
		newnode ( -1 ) ;
		last = 0 ;
		n = 0 ;
		S[n] = -1 ;//Starting with a character that is not in the character set reduces special judgement
		fail[0] = 1 ;
	}

	int get_fail ( int x ) {//Like KMP, find the longest one after mismatch
		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 ) ;//Find the matching position of the palindrome string from the previous palindrome string
		if ( !next[cur][c] ) {//If this palindrome string does not appear, a new essentially different palindrome string appears.
			int now = newnode ( len[cur] + 2 ) ;//New Node
			fail[now] = next[get_fail ( fail[cur] )][c] ;//Create fail pointers like AC automata to jump after mismatch
			next[cur][c] = now ;
			num[now] = num[fail[now]] + 1 ;
		}
		last = next[cur][c] ;
		cnt[last] ++ ;
		id[last]=n;//Where the current palindrome substring ends + 1
	}

	void count () {
		for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
		//Father adds up son's cnt, because if fail[v]=u, u must be a child palindrome string of v!
		//Starting from 2, because 0 and 1 are the root nodes
        for(int i=2;i<p;i++){
            //id[i] is the end + 1, len is the length, so the starting point is id[i]-len[i], and the ending point is id[i]-1.
            //In this way, information about the location and length of all palindrome substrings can be saved.
            if(check(id[i]-len[i],id[i]-1)){
                ans[len[i]]+=cnt[i];
            }
        }
	}
};
Palindromic_Tree PAM;
signed main(){
    pp[0]=1;
    for(int i=1;i<MAXN;i++){
        pp[i]=pp[i-1]*hash1%mod;
    }
    while(scanf("%s",s)!=EOF){
        memset(ans,0,sizeof(ans));
        PAM.init();
        int len=strlen(s);
        ha[0]=s[0];
        for(int i=0;i<len;i++){
            PAM.add(s[i]);
        }
        for (int i=1 ;i<len;i++) {
            ha[i]=((ha[i - 1]*hash1%mod)+s[i])%mod;
        }
        PAM.count();
        printf("%lld",ans[1]);
        for(int i=2;i<=len;i++){
            printf(" %lld",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

Posted by greatepier on Sun, 28 Jul 2019 06:59:58 -0700