UVa 11584
将一个字符串进行划分,使得划分后的每一部分都是回文字符串,求给定字符串的最小划分。
假设已经找到了前end
个字符的最小划分,现在加入第end + 1
个字符,可以将第end + 1
个字符作为结尾,向前寻找所有可能的回文串,设回文串的起点为begin
,那么可以将str[begin] ~ str[end + 1]
作为一个新的划分,和前begin - 1
个字符的最优情况进行组合,得到前end
个字符的划分方法,最后在其中取最小值即可。递推公式为Group[end] = min(Group[begin - 1] + 1, Palindrome[begin][end] == true)
。
如果每次都重新判断回文串,那么在O(n ^ 2)
的复杂度上会再引入O(n)
的复杂度,使得整体复杂度提升到O(n ^ 3)
。为了降低复杂度,可以提前将所有的回文串都计算出来,然后直接判断即可。在字符串中查找所有的回文串的时间复杂度也为O(n ^ 3)
,但是也存在动态规划的方法。
假设已经找到了前end
个字符的所有回文串,现在加入第end + 1
个字符,如果第end + 1
个字符是一个回文串的结尾(长度大于1
),那么第end
个字符也是一个回文串的结尾,其对应的开头为第begin
个字符,如果str[begin - 1] == str[end]
,则str[begin - 1][end]
就是一个回文串。这种方式找到的回文串最小长度为3
,所以长度为1
和2
的回文串需要单独计算。
#include <iostream>
#include <string>
#include <vector>
#include <climits>
using namespace std;
void findPalindrome(const string &str, vector<vector<bool>> &Palindrome)
{
Palindrome[0][0] = true;
for (size_t end = 1; end < str.size(); end++)
{
Palindrome[end][end] = true;
Palindrome[end - 1][end] = Palindrome[end][end - 1] = str[end - 1] == str[end];
for (size_t begin = 1; begin < end; begin++)
{
if (Palindrome[begin][end - 1] && str[begin - 1] == str[end]) {
Palindrome[begin - 1][end] = true;
Palindrome[end][begin - 1] = true;
}
}
}
}
void findMinGroup(const string &str, const vector<vector<bool>> &Palindrome)
{
vector<int> Group(str.size(), INT_MAX);
Group[0] = 1;
for (size_t end = 1; end < str.size(); end++)
{
if (Palindrome[0][end]) Group[end] = 1;
for (size_t begin = 1; begin <= end; begin++)
{
if (Palindrome[begin][end]) {
if (Group[end] > Group[begin - 1] + 1) {
Group[end] = Group[begin - 1] + 1;
}
}
}
}
cout << Group.back() << endl;
}
int main()
{
int n = 0;
string str;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> str;
vector<vector<bool>> Palindrome(str.size(), vector<bool>(str.size(), false));
findPalindrome(str, Palindrome);
findMinGroup(str, Palindrome);
}
return 0;
}
/*
3
racecar
fastcar
aaadbccb
*/