LeetCode 2002. 两个回文子序列长度的最大乘积 DFS+状态压缩DP
2002. 两个回文子序列长度的最大乘积
1.题目:
给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。
请你返回两个回文子序列长度可以达到的 最大乘积 。
子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。
2.思路:
- 暴力dfs
对于字符串中的一个字符,有三种情况
-
放入第一个字符串
-
放入第二个字符串
-
都不放入
暴力递归即可
- 状态压缩DP
在一个数组中,某个元素只有取和不取两种状态,例如【a,b,c】,则共有2^3种状态,【000,001,010,011,100,101,110,111】,如果该位置为1,则表示取该元素,例如110
表示ab
,当两个自己没有相同的元素时,即x&y=0
,例如ac
(101)&b
(010)=0
此时我们便把一个bool数组压缩为一个整数,通过位运算来提高代码效率
对于字符串中的两个子序列,因为不能有相同的字符,所有子序列状态码相与等于0
此时我们直接暴力搜索每两个状态即可
//state_arr=[]存储所有的合法状态
for(int i=0;i<state_arr.size();i++){
for(int j=i+1;j<state_arr.size();j++){
if(state_arr[i]&state_arr[j]==0){
////两个子集为j,j
}
}
}
优化:从大到小枚举每个状态中的子集与补集
for (int i = 1; i < m; i++) {
int split = i >> 1;
// 由于 j 与 i ^ j 是互补的关系,枚举到 i / 2即可,之后的会重复
//j = (j - 1) & i, j-1表示剔除j这个状态最后的一个1,但可能会在后面增加一个1,所有将它与i相与,即可保证j是i的子集
for (int j = (i - 1) & i; j > split; j = (j - 1) & i) {
//Code
//两个子集为j,i^j
}
}
3.Code
- 暴力DFS
class Solution {
int res=0;
public int maxProduct(String s) {
StringBuilder s1 = new StringBuilder(),s2 = new StringBuilder();
this.dfs(s,s1,s2,0);
return res;
}
public void dfs(String s,StringBuilder s1,StringBuilder s2,int index){
if(check(s1) && check(s2)){
res=Math.max(res,s1.length()*s2.length());
}
if(index==s.length()){
return;
}
//加入第一个字符串
s1.append(s.charAt(index));
this.dfs(s,s1,s2,index+1);
s1.deleteCharAt(s1.length()-1);
//加入第二个字符串
s2.append(s.charAt(index));
this.dfs(s,s1,s2,index+1);
s2.deleteCharAt(s2.length()-1);
//不加入
this.dfs(s,s1,s2,index+1);
}
public boolean check(StringBuilder s){
if(s.length()<=0){
return true;
}
for(int i=0,j=s.length()-1;i<s.length();i++,j--){
if(s.charAt(i)!=s.charAt(j)){
return false;
}
}
return true;
}
}
- 状态压缩DP
class Solution {
int res=0;
public int maxProduct(String s) {
int n=s.length();
int m=1<<n;
char[] str=s.toCharArray();
int[] count =new int[m];
//获取每个合法状态的1的位数
for (int i = 0; i < m; i++) {
if(check(s,i)){
count[i]=Integer.bitCount(i);
}
}
//枚举s的每个子序列的每个子集和该子集的补集
for (int i = 0; i <m ; i++) {
for(int j=i;j>0;j=(j-1)&i){
res=Math.max(res,count[j]*count[i^j]);
}
}
return res;
}
public boolean check(String s,int state) {
int l=0,r=s.length()-1;
while(l<r){
while(l<r && ((state>>l&1)==0)){
l++;
}
while(l<r && ((state>>r&1)==0)){
r--;
}
if(s.charAt(l)!=s.charAt(r)){
return false;
}
l++;
r--;
}
return true;
}
}