[CF1081H]Palindromic Magic
题意:给两个字符串\(a,b\),求出有多少种不同的字符串能通过从第一个串中取出一个回文串,从第二个串中取出一个回文串,按顺序拼接得到。
题解:证明?看官方题解吧
一些定义:
回文串拆分:\(s=ab\),其中\(a,b\)都非空且是回文串。
非严格回文串拆分:\(s=ab\),其中\(a,b\)都是回文串,\(b\)非空。
循环串:如果\(s\)的最小满足\(u||S|\)的周期\(u\ne |S|\),则\(s\)是循环串。
引理1:如果\(p,q\)是字符串\(s\)的周期,\(p+q \le s\),那么\(\gcd(p,q)\)也是\(s\)的周期。
引理2:设\(S\)为\(s\)长度\(\le \frac{|S|}{2}\)的周期集合,那么\(\forall u \in S,\min(S) | u\)。
引理3:如果\(s=x_1x_2=y_1y_2=z_1z_2,|x_1|<|y_1|<|z_1|,x_2,y_1,y_2,z_1\)为非空回文串,那么\(x_1,z_2\)也是回文串。
引理4:如果\(s\)存在回文串拆分,设\(a\)为\(s\)的最长回文前缀,\(s=ax\),\(b\)为\(s\)的最长回文后缀,\(s=yb\),那么\(x,y\)中至少有一个是回文串。
引理5:设\(s=p_1q_1=p_2q_2(|p_1|<|p_2|),p_1,q_1,p_2,q_2\)是回文串,\(q_1,q_2\)非空,那么\(s\)是循环串。
引理6:设\(s=p_1q_1=p_2q_2=\dots=p_tq_t\)为所有的非严格回文串拆分,\(h\)是\(s\)最小的满足\(h||s|\)的周期。如果\(t\ne 0\),则\(t=\frac{|s|}h\)。
引理7:设\(s=p_1q_1=p_2q_2=\dots=p_tq_t\)为所有的非严格回文串拆分,\(|p_i|<|p_{i+1}|\)。那么\(\forall i \in [0,t-1]\),\(p_i=\text{border}(p_{i+1})\)和\(q_{i+1}=\text{border}(q_i)\)中至少有一个成立。
回到原问题。
最开始的想法就是求出\(a,b\)中回文串的个数,乘起来就是答案。但是这样有一些串会被计算多次。根据引理7,设\(s=xy\)是一个在答案中的串,如果把\(x\)变为\(\text{border}(x)\)或\(y\)变为\(\text{border}(y)\)也能形成\(s\),那么答案减1。
讨论\(x\)变为\(\text{border}(x)\)的情况,另一种显然是对称的。
设\(x=\text{border}(x)w\),则要计算的就是\(b\)中\(T\)的个数,满足\(T=wS\),\(S,T\)都是非空回文串。因为\(w\)是\(x\)的最小周期,所以\(w\)不可能是循环串。
分类讨论一下:
1.\(|w|>|S|\)。因为\(w\)不是循环串,根据引理5,\(w\)最多存在一种非严格回文串拆分。而对于合法的\(w\),因为\(T=wS\)是回文串,所以\(w=SU\)是\(w\)的一种回文串拆分。根据引理4,我们可以通过求出\(w\)的最长回文前缀/后缀找到这种拆分,通过哈希求\(b\)中是否存在匹配的\(wS\)。这里要注意\(S\)不能为空串,所以\(w\)是回文串的话应该直接continue。
2.\(|w|\le|S|\)。这种情况下\(|w|\le\frac T2\),如果\(S\)不是\(T\)的\(\text{border}\),那么说明\(T\)存在一个\(<|w|\)的周期,根据引理2,\(w\)是循环串,产生矛盾。所以只要对\(b\)预处理所有满足\(S=\text{border}(T)\)的\(S,T\)二元组,通过哈希求个数即可。
对\(a,b\)分别做一遍,然后还要加上把\(x\)变为\(\text{border}(x)\)或\(y\)变为\(\text{border}(y)\)都能形成\(s\)的个数。直接用哈希求出匹配的\(w\)对数即可。
找回文串显然可以通过回文树,找子串最长回文前后缀可以在回文树上倍增。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef pair<int, int> pii;
#define mp make_pair
#define fi first
#define se second
const int mod1 = 998244353;
const int mod2 = 998244853;
const int base = 131;
int gi() {
int x = 0, o = 1;
char ch = getchar();
while((ch < '0' || ch > '9') && ch != '-') {
ch = getchar();
}
if(ch == '-') {
o = -1, ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0', ch = getchar();
}
return x * o;
}
pii operator+(pii a, pii b) {
return mp((a.fi + b.fi) % mod1, (a.se + b.se) % mod2);
}
pii operator-(pii a, pii b) {
return mp((a.fi - b.fi + mod1) % mod1, (a.se - b.se + mod2) % mod2);
}
pii operator*(pii a, pii b) {
return mp(1ll * a.fi * b.fi % mod1, 1ll * a.se * b.se % mod2);
}
pii operator*(pii a, int b) {
return mp(1ll * a.fi * b % mod1, 1ll * a.se * b % mod2);
}
pii operator+(pii a, int b) {
return mp((a.fi + b) % mod1, (a.se + b) % mod2);
}
int n, m;
char a[N], b[N];
pii ha[N], hb[N], pw[N];
long long ans = 0;
struct PAM {
int ch[N][26], fa[N], len[N], tot = 1, last = 1, anc[20][N], pos[N], r[N];
char s[N];
void extend(int n, int c) {
int p = last;
while(s[n - len[p] - 1] != s[n]) {
p = fa[p];
}
if(!ch[p][c]) {
int v = ++tot;
len[v] = len[p] + 2;
int k = fa[p];
while(s[n - len[k] - 1] != s[n]) {
k = fa[k];
}
fa[v] = ch[k][c];
ch[p][c] = v;
}
last = ch[p][c];
pos[n] = last;
r[last] = n;
}
void build(char *t) {
int n = strlen(t + 1);
for(int i = 0; i <= n + 1; i++) {
s[i] = t[i];
}
fa[0] = fa[1] = 1, len[1] = -1;
for(int i = 1; i <= n; i++) {
extend(i, s[i] - 'a');
}
for(int i = 0; i <= tot; i++) {
anc[0][i] = fa[i];
}
for(int i = 1; i <= 18; i++)
for(int j = 0; j <= tot; j++) {
anc[i][j] = anc[i - 1][anc[i - 1][j]];
}
}
int jump(int r, int l) {
int u = pos[r];
if(len[u] <= l) {
return len[u];
}
for(int i = 18; ~i; i--) if(len[anc[i][u]] > l) {
u = anc[i][u];
}
return len[fa[u]];
}
bool check(int r, int l) {
return jump(r, l) == l;
}
} A, B, RA, RB;
map<pii, int> fa, fb, ga, gb;
pii gethsh(pii *hsh, int l, int r) {
return hsh[r] - hsh[l - 1] * pw[r - l + 1];
}
int main() {
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
#endif
scanf("%s", a + 1), scanf("%s", b + 1);
n = strlen(a + 1), m = strlen(b + 1);
pw[0] = mp(1, 1);
for(int i = 1; i <= n || i <= m; i++) {
pw[i] = pw[i - 1] * base;
}
for(int i = 1; i <= n; i++) {
ha[i] = ha[i - 1] * base + (a[i] - 'a' + 1);
}
for(int i = 1; i <= m; i++) {
hb[i] = hb[i - 1] * base + (b[i] - 'a' + 1);
}
A.build(a), B.build(b);
reverse(a + 1, a + n + 1), reverse(b + 1, b + m + 1);
RA.build(a), RB.build(b);
ans = 1ll * (A.tot - 1) * (B.tot - 1);
for(int i = 2; i <= A.tot; i++) {
++fa[gethsh(ha, A.r[i] - A.len[i] + 1, A.r[i])];
int p = A.fa[i];
if(p < 2) {
continue;
}
if(A.len[i] <= (A.len[p] << 1)) {
++ga[gethsh(ha, A.r[i] - (A.len[i] - A.len[p]) + 1, A.r[i])];
}
}
for(int i = 2; i <= B.tot; i++) {
++fb[gethsh(hb, B.r[i] - B.len[i] + 1, B.r[i])];
int p = B.fa[i];
if(p < 2) {
continue;
}
if(B.len[i] <= (B.len[p] << 1)) {
++gb[gethsh(hb, B.r[i] - B.len[i] + 1, B.r[i] - B.len[p])];
}
}
for(int i = 2; i <= A.tot; i++) {
int p = A.fa[i];
if(p < 2) {
continue;
}
int l = A.r[i] - (A.len[i] - A.len[p]) + 1, r = A.r[i];
ans -= gb[gethsh(ha, l, r)];
int lps = A.jump(r, r - l + 1);
if(lps == r - l + 1) {
continue;
}
if(RA.check(n - l + 1, r - l + 1 - lps)) {
if(fb.count(gethsh(ha, l, r)*pw[r - l + 1 - lps] + gethsh(ha, l, r - lps))) {
--ans;
}
continue;
}
int lpp = RA.jump(n - l + 1, r - l + 1);
if(A.check(r, r - l + 1 - lpp)) {
if(fb.count(gethsh(ha, l, r)*pw[lpp] + gethsh(ha, l, l + lpp - 1))) {
--ans;
}
}
}
for(int i = 2; i <= B.tot; i++) {
int p = B.fa[i];
if(p < 2) {
continue;
}
int l = B.r[i] - B.len[i] + 1, r = B.r[i] - B.len[p];
ans -= ga[gethsh(hb, l, r)];
int lps = B.jump(r, r - l + 1);
if(lps == r - l + 1) {
continue;
}
if(RB.check(m - l + 1, r - l + 1 - lps)) {
if(fa.count(gethsh(hb, r - lps + 1, r)*pw[r - l + 1] + gethsh(hb, l, r))) {
--ans;
}
continue;
}
int lpp = RB.jump(m - l + 1, r - l + 1);
if(B.check(r, r - l + 1 - lpp)) {
if(fa.count(gethsh(hb, l + lpp, r)*pw[r - l + 1] + gethsh(hb, l, r))) {
--ans;
}
}
}
ga.clear(), gb.clear();
for(int i = 2; i <= A.tot; i++) {
int p = A.fa[i];
if(p < 2) {
continue;
}
++ga[gethsh(ha, A.r[i] - (A.len[i] - A.len[p]) + 1, A.r[i])];
}
for(int i = 2; i <= B.tot; i++) {
int p = B.fa[i];
if(p < 2) {
continue;
}
++gb[gethsh(hb, B.r[i] - B.len[i] + 1, B.r[i] - B.len[p])];
}
for(auto x : ga) {
ans += 1ll * x.se * gb[x.fi];
}
cout << ans;
return 0;
}
[CF1081H]Palindromic Magic的更多相关文章
- hihocoder #1052 基因工程
传送门:基因工程 这道题拖了好久,一直没有清晰的思路. 当然,$K\le\frac{N}{2}$时,比较简单.下面我着重讲一下当$K>\frac{N}{2}$,即前$K$个字符与后$K$个字符有 ...
- 最长回文子串-LeetCode 5 Longest Palindromic Substring
题目描述 Given a string S, find the longest palindromic substring in S. You may assume that the maximum ...
- leetcode--5. Longest Palindromic Substring
题目来自 https://leetcode.com/problems/longest-palindromic-substring/ 题目:Given a string S, find the long ...
- [LeetCode] Longest Palindromic Substring 最长回文串
Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...
- Codeforces CF#628 Education 8 D. Magic Numbers
D. Magic Numbers time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...
- ACM: Gym 101047B Renzo and the palindromic decoration - 手速题
Gym 101047B Renzo and the palindromic decoration Time Limit:2000MS Memory Limit:65536KB 64 ...
- [8.3] Magic Index
A magic index in an array A[0...n-1] is defined to be an index such that A[i] = i. Given a sorted ar ...
- No.005:Longest Palindromic Substring
问题: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...
- Python魔术方法-Magic Method
介绍 在Python中,所有以"__"双下划线包起来的方法,都统称为"Magic Method",例如类的初始化方法 __init__ ,Python中所有的魔 ...
随机推荐
- Delphi XE2 之 FireMonkey 入门(29) - 数据绑定: TBindingsList: 表达式的 Evaluate() 方法
Delphi XE2 之 FireMonkey 入门(29) - 数据绑定: TBindingsList: 表达式的 Evaluate() 方法 TBindingsList 中可能不止一个表达式, 通 ...
- GMSSL学习总结1
接触GMSSL一段时间了,总结一点点想法 证书:DER格式.PEM格式 .DER = DER扩展用于二进制DER编码证书. .PEM = PEM扩展用于不同类型的X.509v3文件,是以“ - BEG ...
- 【AndroidFramework】【EMMC拷机】混合拷机时盒子待机
[AndroidFramework][EMMC拷机]混合拷机时盒子待机 问题结论 暂时确定为误按键,不是故障.问题关闭.后续完全保证无按键(物理隔离)情况下如果出现待机,请反馈. 问题描述 [EMMC ...
- hackinglab 脚本关 writeup
地址:http://hackinglab.cn 脚本关 key又又找不到了 点击提供的链接后,实际发生了两次跳转,key 在第一次跳转的网页中,key is : yougotit_script_now ...
- [Python3 练习] 006 汉诺塔2 非递归解法
题目:汉诺塔 II 接上一篇 [Python3 练习] 005 汉诺塔1 递归解法 这次不使用递归 不限定层数 (1) 解决方式 利用"二进制" (2) 具体说明 统一起见 我把左 ...
- Notepad++-第一篇命令行语句执行之编译、运行Java
1.让Notepad++编译和运行Java,在电脑上要已经配置好了Java的开发环境 2.在Notepad++上面的选项栏中找到 Plugins--->Plugin Admin 3.在Avail ...
- luogu 3426题解 (KMP)
题面 Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段作为模版. 然后将模版重复喷涂到相应的位置后就得到了他想要的字符序列.一个字符可以被喷涂很多次,但是一个位置 ...
- 数组去重,排序,重复次数,两个数组合并,两个数组去重,map(),filter(),reduce()
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- TP5.1+Vue前后端分离实践
配置: 主域名 www.demo.xyz 二级子域名 api.demo.xyz 列表项目其中api.demo.xyz加入了版本控制,使用的是URL路由传入方式 在route.php路由文件中配置,如下 ...
- 问题 B: 傻鸡抓大闸蟹
问题 B: 傻鸡抓大闸蟹 时间限制: 1 Sec 内存限制: 128 MB提交: 94 解决: 39[提交] [状态] [命题人:jsu_admin] 题目描述 背景又到了吃大闸蟹的季节,黄老师想 ...