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 凤凰工作室
标签:Partitioning,code,int,题解,复杂度,异或,优化,dp,回文 From: https://www.cnblogs.com/dijkstraphoenix/p/18382970