[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;
}

猜你喜欢

转载自www.cnblogs.com/gczdajuruo/p/11033191.html
今日推荐