题意:给两个字符串\(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的更多相关文章

  1. hihocoder #1052 基因工程

    传送门:基因工程 这道题拖了好久,一直没有清晰的思路. 当然,$K\le\frac{N}{2}$时,比较简单.下面我着重讲一下当$K>\frac{N}{2}$,即前$K$个字符与后$K$个字符有 ...

  2. 最长回文子串-LeetCode 5 Longest Palindromic Substring

    题目描述 Given a string S, find the longest palindromic substring in S. You may assume that the maximum ...

  3. leetcode--5. Longest Palindromic Substring

    题目来自 https://leetcode.com/problems/longest-palindromic-substring/ 题目:Given a string S, find the long ...

  4. [LeetCode] Longest Palindromic Substring 最长回文串

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  5. 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 ...

  6. ACM: Gym 101047B Renzo and the palindromic decoration - 手速题

     Gym 101047B  Renzo and the palindromic decoration Time Limit:2000MS     Memory Limit:65536KB     64 ...

  7. [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 ...

  8. No.005:Longest Palindromic Substring

    问题: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

  9. Python魔术方法-Magic Method

    介绍 在Python中,所有以"__"双下划线包起来的方法,都统称为"Magic Method",例如类的初始化方法 __init__ ,Python中所有的魔 ...

随机推荐

  1. 建立 Active Directory域 ----学习笔记

    第五章 建立 Active Directory域 1.工作组和域的理解 ​ a.工作组是一种平等身份环境,各个计算机之间各个为一个独立体,不方便管理和资源共享. ​ b.域环境一般情况下满足两类需求, ...

  2. JavaSE编码试题强化练习7

    1.编写应用程序,创建类的对象,分别设置圆的半径.圆柱体的高,计算并分别显示圆半径.圆面积.圆周长,圆柱体的体积. /** * 圆类 */ public class Circle { /** * 类属 ...

  3. [Python3] 014 集合的内置方法

    目录 1. Python3 中如何查看 set() 的内置方法 2. 少废话,上例子 (1) add() (2) 又见清理大师 clear() (3) 又见拷贝君 copy() (4) 找茬君 dif ...

  4. 日记smarthome

    测试命令:测试命令 7e 7e 两个字节 一个字节  两个字节 一个字节     解释: 两个字节是userid的值 int Userid = data[i] * 256 + data[i + 1]; ...

  5. C++解析XML字符串

    项目交互遇到了需要VC++中解析XML字符串,故花了点时间了解了下VC++中解析XML的诸多方法主要包括三种:msxml(微软提供).markup.TinyXml. 开始花了点时间使用msxml3,虽 ...

  6. js如何获取到select的option值???

    1.获得选项option的值 var obj = document.getElementByIdx_x(”testSelect”); //定位id var index = obj.selectedIn ...

  7. Nodejs中request出现ESOCKETTIMEDOUT解决方案

    做需求的时候,使用Nodejs的request批量请求某一个接口,由于接口超时,出现 ESOCKETTIMEDOUT,程序中断 为了让程序遇到 ESOCKETTIMEDOUT 之后能够继续执行下去,需 ...

  8. Oracle之共享服务器模式

    在共享服务器体系结构中,一个dispatcher分派器将传入网络的多个会话请求定向到一个共享服务器进程池,消除了为每个连接分配一个专用服务器进程的需要.作为一般的指导原则,仅当系统需要并发连接到数据库 ...

  9. springboot多数据源&动态数据源(主从)

    多数据源 使用Spring Boot时,默认情况下,配置DataSource非常容易.Spring Boot会自动为我们配置好一个DataSource. 如果在application.yml中指定了s ...

  10. python学习笔记(7): 面向对象

    class Foo: #类中的函数 def bar(self): #功能阐述 print('Bar') pass def hello(self,name): print('i am %s' %name ...