行路难,行路难,多歧路,今安在?长风破浪会有时,直|

hhhqx

园龄:1年8个月粉丝:9关注:14

题解:CF1827C Palindrome Partition

CF1827C Palindrome Partition 题解

题面

题目传送门

称一个字符串是好的,当且仅当它是一个长度为偶数的回文串或由若干长度为偶数的回文串拼接而成。

给定一个长度为 n 的字符串 s,求有多少 s 的子串是好的。

$ 1 \le n \le 5 \cdot 10^5s$ 仅包含小写字母。

与 CSP-S 2023 T2 的区别

千万不要说这两题重了,免得别人来笑话。

举一个例子,abbcca,可以思考一下。

分析

那么这题多半是只能老老实实去找回文串。考虑使用回文树

先来分析回文串的性质。

  • s[l,r] 为回文串,若 s[x,r] 也为回文串(l+r+12xr)。
  • s[l,l+(rx+1)1] 也为回文串(因为整体回文,所以右边会问则左边也回文)。
  • s[l+(rx+1),x1] 也为回文串(因为整体回文)。

如果通过第一个假设,判断 s[l,r] 是好的就不需要判断 s[l,r] 是否回文,只用判断它内部是否可以组成。

思路

我们知道在字符串位置 i,在回文树位置 j,只用不断跳后缀链接,设当前位置回文串的长度为 l,则 s[il+1,i] 为回文串。

设对于所有 l偶数,组成一个序列 L1<L2<L3<<Lk,那么 s[iLx+1,i]2xk)必定可以被除自己的回文串组成(通过分析容易知道)。

那么我们只用对于每个 i 找到 L1(可以用路径压缩来解决满足复杂度要求),则 dpi=dpL1+1,若没有 L1,则 dpi=0

最后答案为 x=1ndpx

时间复杂度 O(nlogn),瓶颈在找到 L1

Code

#include <bits/stdc++.h>

using namespace std;
using LL = long long;
using Pair = pair<LL, LL>;

const int MAXN = 1e6 + 3;

int n;
string s;
int trie[MAXN][26], cv = 1, len[MAXN], _len[MAXN], pos[MAXN]/*原串中出现位置*/, nxt[MAXN]/*后缀链接*/;
int fa[MAXN];
LL dp[MAXN], ans = 0;

int Getf(int x){
  if(fa[x] <= 1|| fa[x] == x || Getf(fa[x]) == 0 || _len[Getf(fa[x])] == 0){
    return (len[x] % 2 == 0 ? fa[x] = x : fa[x] = 0);
  }else return fa[x] = Getf(fa[x]);
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
  int T;
  cin >> T;
  while(T--){
    cin >> n >> s, s = " " + s;
    for(int i = 0; i <= cv; i++){
      for(int col = 0; col < 26; col++) trie[i][col] = 0;
    }
    cv = 1;
    ans = 0;
    _len[0] = len[0] = -1, nxt[0] = 0, _len[1] = len[1] = 0, nxt[1] = 0;
    for(int i = 1, j = 1 /* i-1 的后缀的最长回文后缀对应回文树上的节点编号 */; i <= n; i++){
      while(s[i] != s[i - len[j] - 1]) j = nxt[j];
      if(!trie[j][s[i] - 'a']){
        trie[j][s[i] - 'a'] = ++cv;
        _len[cv] = len[cv] = len[j] + 2;
        pos[cv] = i - len[cv] + 1;
        for(int &k = (nxt[cv] = nxt[j]); s[i] != s[i - len[k] - 1]; k = nxt[k]);
        // 也可以写成 for(int &k = nxt[cv] = nxt[j]; s[i] != s[i - len[k] - 1]; k = nxt[k]);

        if(len[cv] > 1) fa[cv] = nxt[cv] = trie[nxt[cv]][s[i] - 'a'];
        else fa[cv] = nxt[cv] = 1;
      }
      j = trie[j][s[i] - 'a'];
      if(Getf(j) != 0) _len[j] = _len[Getf(j)];
      else _len[j] = 0;
      dp[i] = (_len[j] == 0 ? 0 : dp[i - _len[j]] + 1), ans += dp[i];
    }
    cout << ans << "\n";
  }
  return 0;
}
/*
6
abaaba
1 2 1 1 1
1 2 0 1 1**
2 3 1 1 1
2 3 0 1 1**
3 4 2 2 3
3 4 0 2 3**
4 5 2 2 2
*/

本文作者:hhhqx

本文链接:https://www.cnblogs.com/huangqixuan/p/18584537

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hhhqx  阅读(8)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起