【题目描述如下】
给你一个字符串 s
,请你找到 s
中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。
请你返回两个回文子序列长度可以达到的 最大乘积 。
子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。
【分析】
从提示来看,数据量不大,但是字符串的组合很多,每个字符都有选和不选两种情况,最大有1 << 12 - 1个子串,需要对每个子串进行是否为回文串的判断。
可以采用状态压缩来做,用二进制位标记每个字符的选与不选,遍历整个状态空间,枚举所有字符串;
针对每个字符串,对其补集的子集进行遍历;
【代码如下】
class Solution {
public:
/*
由于数据量不大,可以枚举所有的状态空间,使用二进制表示的方式进行状态压缩,
每个二进制位标志字符串中的每个字符;
*/
bool IsPal(string& s)
{
int i = 0;
int j = s.size() - 1;
while (i < j) {
if (s[i++] != s[j--]) {
return false;
}
}
return true;
}
int maxProduct(string s) {
int res = 0;
int n = s.size();
bool allSubStr[1 << 12] {false};
int subStrLen[1 << 12] {0};
for (int i = 1; i < 1 << n; i++) {
string subStr;
for (int j = 0; j < n; j++) {
if ((i >> j) & 0x1) {
subStr.push_back(s[j]);
}
}
if (IsPal(subStr)) {
allSubStr[i] = true;
subStrLen[i] = subStr.size();
}
}
for (int i = 1; i < 1 << n; i++) {
if (!allSubStr[i]) {
continue;
};
// 求补集,枚举补集的所有子集,采用l = (l - 1) & left遍历子集
int left = i ^ ((1 << n) - 1);
for (int l = left; l != 0; l = (l - 1) & left) {
if (allSubStr[l]) {
res = max(res, subStrLen[i] * subStrLen[l]);
}
}
}
return res;
}
};