AT_code_festival_2017_qualc_d - Yet Another Palindrome Partitioning 题解

Yet Another Palindrome Partitioning 题解

题目大意

给出一个字符串,求把这个字符串划分成最少的小段,使每个小段都可以经过字母重组后为回文串。

题目分析

如果暴力的话,需要 DFS 段数、每一段的左节点、右节点,以及判断是否为回文串,时间复杂度在 \(O(|S|^{|S|})\) 左右。

但是本题需要将字符串划分成若干子串,那么就可以考虑 DP。那么可以写出一个状态转移方程:

\[dp_i=\min(dp_j+1) ,j \leq i,S_{i\sim j}为回文串 \]

其中 \(dp_i\) 表示 \(S\) 的前 \(i\) 个字符被划分成满足题意的子串最少的划分数,\(i\) 是当前这段的右节点,\(j\) 是需要匹配的左节点。要使答案最小,肯定是取 \(\min\) 值。

时间复杂度 \(O(|S|^3)\) (这里判断回文还有线性的时间复杂度)。

那么考虑优化:

优化 1

首先判断回文的线性复杂度是非常可恶的,必须优化掉。

传统的判断回文是将字符串左右对比,那么有没有一种办法可以 \(O(1)\) 对比呢?我们可以使用异或进行优化。根据异或的性质,\(a \oplus b \oplus b=a\),那么左右相同的字符就会抵消掉。为了不出现冲突(即两个不同的字符异或后有一部分变了),需要给每一个字符一个特定的值,将 a\(2^0\),给 b\(2^1\) \(......\)

那么回文串就有两种情况:

1.例如 abcba 一样的,奇数个字符的回文串,异或优化后会剩下中间的字符(即剩下 \(2^{x}\))。

2.例如 abba 一样的,偶数个字符的回文串,异或优化后什么也不剩了(即剩下 0)。

由于本题只考虑小写英文字母,时间复杂度 \(O(26|S|^2)\)

优化 2

有一点记忆化的思想,将 \(dp_i\) 的值存储起来,每次更新更优的,就可以优化取 \(\min\) 值的时间。

最后还需要用 bitset 优化 \(dp\) 数组,否则要爆(bitset 可以 \(O(1)\) 对整个数组进行位运算)。

代码放上来

#include<bits/stdc++.h>
using namespace std;

string s;
int dp[200005];
int xop[200005];//存储每个字符值的前缀异或
int w[1<<26];//存储每个字符的最优 dp 值

int main(void)
{
    memset(w,0x7f,sizeof(w));
    w[0]=0;
    cin>>s;
    for(int i=0;i<s.length();i++)
        xop[i]=(i==0?1<<s[i]-'a':xop[i-1]^(1<<s[i]-'a'));//预处理前缀异或值
    for(int i=0;i<s.length();i++)
    {
        dp[i]=w[xop[i]];
        for(int j=0;j<26;j++)
            dp[i]=min(dp[i],w[xop[i]^(1<<j)]);
        w[xop[i]]=min(w[xop[i]],dp[i]);//更新更优的 dp 值
        dp[i]++;
    }
    cout<<dp[s.length()-1];
    return 0;
}

本题完结散花

Solution By Luogu 凤凰工作室

posted @ 2024-08-27 16:30  DijkstraPhoenix  阅读(6)  评论(0编辑  收藏  举报