JSOI2013 快乐的JYY(manacher,回文串)

萌新不会PAM
于是用manacher+hash
O(nlogn)过了这题
首先我们有一个结论
对于一个长度n的串,它的本质不同的回文子串数量是O(n)的

然后我们先用manacher算出半径r[i]
然后开一个数组记录一下最长回文子串在原字符串中哪个位置

我们考虑任意一个>=3的回文子串不论是奇是偶,去掉头尾它依然是回文串
又因为只有O(n)个本质不同的回文子串(以下简称子串),所以我们可以考虑用字符串哈希map给每个本质不同的子串标号,然后每个子串的节点把去掉头尾的子串的节点当作父亲,就类似于AC自动机的fail树,意即你访问子节点的串同时一定遍历了父节点的串,然后只有长度为1或2的子串向根连边(把根当作父亲).
于是这就形成了一颗树

于是我们每遍历一个以某个位置为对称轴(可以是字母也可以是两个字母间的空隙)的最长回文子串就给它打个标记,一个回文子串遍历了多少次显然是它树上的结点的子树和,于是我们可以算出每个回文子串在A中出现了几次,同理对B建树,然后把相同的子串在两树中的方案乘起来再累加到ans里,就做完了

/*快乐的JYY*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
#define ll long long
const int maxn = 2e5 + 10; 
#define ul unsigned long long
ul mi[maxn];
ll ans = 0;
int head[maxn];
int idcnt = 0;
struct Edge{
	int nxt,point;
}edge[maxn*2];
int tot;
void add(int x,int y){
	edge[++tot].nxt = head[x];
	edge[tot].point = y;
	head[x] = tot;
}
struct Str{
	char str[maxn];	char s[maxn*2];
	int r[maxn] ;int bit[maxn];ul hash[maxn];map<ul,int>id;
	ul v[maxn];
	ll dp[maxn * 2];
	map<ul,ll>f;
	int len;
	int cnt;
	int rt;
	#define p 133331
	void init(){
		for(int i = 1; i <= len; ++i)
			hash[i] = hash[i-1] * p + str[i] - 'A' + 1;
	}
	ul calc(int l,int r){
		int k = r - l + 1;
		return hash[r] - mi[k] * hash[l-1];
	}
	void manacher(){
		s[++cnt] = '~',s[++cnt] = '#';
		for(int i = 1; i <= len; ++i){
			s[++cnt] = str[i];	s[++cnt] = '#';
			bit[cnt] = i;
		}
		s[++cnt] = '!';
		s[cnt+1] = '\0';
		int mr = 0,mid = 0;
		for(int i = 2; i <= cnt - 1; ++i){
			if(i <= mr)		r[i] = min(r[(mid<<1)-i],mr - i + 1);
			else	r[i] = 1;
			while(s[i+r[i]] == s[i-r[i]])		r[i]++;
			if(i + r[i] > mr){
				mr = i + r[i] - 1;
				mid = i;
			}
		} 
	}
	void build(){
		rt = ++idcnt;
		for(int i = 2; i <= cnt - 1; ++i){
			int L = i - r[i] + 1,R = i + r[i] - 1;
			if(L == R && s[L] == '#')	continue;
			L = bit[L] + 1,R = bit[R];
			int lst = 0;
			while(L <= R){	
				ul val = calc(L,R);
				if(id.find(val) == id.end()){
					id[val] = ++idcnt;
					v[idcnt] = val;
					if(lst)		add(id[val],lst);
				}
				else{
					if(lst)		add(id[val],lst);
					break;
				}
				lst = id[val];
				if(L == R){
					add(rt,id[val]);
				} 
				if(R == L + 1){
					add(rt,id[val]);
				}
				L++,R--;
			}
			L = i - r[i] + 1,R = i + r[i] - 1;
			L = bit[L] + 1,R = bit[R];
			ul val = calc(L,R);
			dp[id[val]]++;
		}
	}
	void dfs(int x){
		for(int i = head[x]; i ; i = edge[i].nxt){
			int y = edge[i].point;
			dfs(y);
			dp[x] += dp[y];
		}
		f[v[x]] += dp[x];
	}
}A,B;
void init(){
	mi[0] = 1;
	for(int i = 1; i <= 1e5; ++i)
		mi[i] = mi[i-1] * p;
	scanf("%s",A.str+1);
	A.len = strlen(A.str + 1);
	A.init();
	A.manacher();
	A.build();
	A.dfs(A.rt);
	scanf("%s",B.str+1);
	B.len = strlen(B.str + 1);
	B.init();
	B.manacher();
	B.build();
	B.dfs(B.rt);
}
void solve(int x){
	if(x != A.rt){
		ans += A.f[A.v[x]] * B.f[A.v[x]];
	}
	for(int i = head[x]; i ; i = edge[i].nxt){
		int y = edge[i].point;
		solve(y);
	}
}
int main()
{
	init();
	solve(A.rt);
	printf("%lld\n",ans);
	return 0;
}

  

posted @ 2020-08-12 21:07  y_dove  阅读(172)  评论(0编辑  收藏  举报