LeetCode 2002. 两个回文子序列长度的最大乘积 DFS+状态压缩DP

2002. 两个回文子序列长度的最大乘积

1.题目:

给你一个字符串 s ,请你找到 s 中两个 不相交回文子序列 ,使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符,则它们是 不相交 的。

请你返回两个回文子序列长度可以达到的 最大乘积 。

子序列 指的是从原字符串中删除若干个字符(可以一个也不删除)后,剩余字符不改变顺序而得到的结果。如果一个字符串从前往后读和从后往前读一模一样,那么这个字符串是一个 回文字符串 。

2.思路:

  • 暴力dfs

对于字符串中的一个字符,有三种情况

  1. 放入第一个字符串

  2. 放入第二个字符串

  3. 都不放入

    暴力递归即可

  • 状态压缩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;
    }
}
posted @ 2021-11-26 14:54  流光之中  阅读(71)  评论(0编辑  收藏  举报