2019 Multi-University Training Contest 2:I Love Palindrome String(回文树(模板) + 字符串hash(模板)或manacher)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6599
题目大意:给你一个串让你求 s [ l , r ] s[l,r] s[l,r]是回文串且 s [ l , l + r 2 ] s[l,\frac{l + r}{2}] s[l,2l+r]也是回文串的子串数目。
输出答案时要按长度划分答案,也就是按r - l + 1划分答案然后依次输出所有长度的满足条件的子串数目。
2019 Multi-University Training Contest 2:I Love Palindrome String(回文树(模板) + 字符串hash(模板)或manacher)_第1张图片

题解:裸板题,用回文树求出所有不同质的回文串的数量,然后对于每一种回文串,check一下判断满不满足题目要求即可,check可以用字符串hash也可以用马拉车跑出半径来check。

回文树 + 字符串hash

#include
using namespace std;
const int maxn = 6e5 + 10;
char tmp[maxn],str[maxn];
typedef long long ll;
typedef unsigned long long ull;
const ull h1 = 201326611;
const ull h2 = 50331653;
ull h[maxn], p[maxn];
ll ans[maxn];
ull Hash(int l, int r) {
	if (l == 0) return h[r];
	return h[r] - h[l - 1] * p[r - l + 1];
}
bool check(int l, int r) {
    int len = r - l + 1;
    int mid = (l + r) >> 1;
    if (len & 1) return Hash(l, mid) == Hash(mid, r);
    else return Hash(l, mid) == Hash(mid + 1, r);
}
struct Palindromic_tree {
	int nxt[maxn][28];
	int len[maxn];
	int num[maxn];
	int cnt[maxn];
	int fail[maxn];
	int s[maxn];
	int id[maxn];
	int last;
	int p;
	int n;
	int newnode(int length) {
		for(int i = 0; i < 28; i++) nxt[p][i] = 0;
		num[p] = cnt[p] = 0;
		len[p] = length;
		return p++;
	}
	void init() {
		n = 0;
		p = 0;
		last = 0;
		newnode(0);
		newnode(-1);
		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 ( !nxt[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = nxt[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
            nxt[cur][c] = now ;
            num[now] = num[fail[now]] + 1 ;
        }
        last = nxt[cur][c] ;
        id[last] = n;
        cnt[last]++ ;		
	}
	void count() {
		for(int i = p - 1; i >= 0; i--) cnt[fail[i]] += cnt[i];
		for(int i = 2; i < p; i++) {
			if(check(id[i] - len[i],id[i] - 1)) {
				ans[len[i]] += cnt[i];
			}
		}
	}
}t; 
int main() {
	p[0] = 1;
	for(int i = 1; i < maxn; i++)
		p[i] = h1 * p[i - 1];
	while(~scanf("%s",str)) {
		memset(ans,0,sizeof ans);
		int l = strlen(str);
		t.init();
		for(int i = 0; i < l; i++)
			t.add(str[i]);
		h[0] = str[0]; 
		for(int i = 1; i < l; i++)
			h[i] = h[i - 1] * h1 + str[i];
		t.count();
		for(int i = 1; i <= l; i++) {
			if(i - 1) printf(" ");
			printf("%lld",ans[i]);
		}
		printf("\n");
	}
	return 0;
}

(标程给的是马拉车+回文树,思路差不多,但自己写了一发就是RE,遂放弃)。

回文树(待刷):
回文树是一种类似AC自动机的玩意,有fail指针,类似trie的树形结构,O(n)的空间,每一个结点代表一种不同质的回文串,最多 n 种不同质的回文串。板子就是上面一坨。

它的大致思想和过程见大佬博客:https://blog.csdn.net/lwfcgz/article/details/48739051
其实不停的找失配指针是一个有点抽象的东西,因为过程看起来是在不停的找最长回文后缀,实际过程是在树上跳来跳去,每一个fail指针指向的是最长回文后缀(不包括自己)。

参考代码自己按过程画一个图就了解了。
例如 ababa 的回文树构建过程。

推荐几篇大佬博客:
https://blog.csdn.net/u013368721/article/details/42100363 (有图,变量名意义解释)
https://blog.csdn.net/lwfcgz/article/details/48739051 (有算法思想过程图)

你可能感兴趣的:(回文树,字符串,Manacher,字符串哈希)