B站左程云算法视频高级班06

题目一:判定一个由[a - z]字符构成的字符串和一个包含'?'和'*'通配符的字符串是否匹配

?匹配任意单一字符

*匹配任意多个字符包括0个字符

输入描述:字符串str和包含通配符的字符串pattern,1小于等于字符串长度小于等于100

输出描述:true表示匹配,false表示不匹配

str(小写字母)

exp(小写字母、*、.)

boolean f(str , exp, si, ei)

public static boolean isValid(char[] s, char[] e){
    for (int i = 0; i < s.length; i++){
        if(s[i] == '*' || s[i] == '.'){
            return false;
        }
    }
    for (int i = 0; i < e.length; i++){
        if(e[i] == '*' && (i == 0 || e[i - 1] == '*')){
            return false;
        }
    }
    return true;
}
public static boolean isMatch(String str,String exp){
    if(str == null || exp = null){
        return false;
    }
    char[] s = str.toCharArray();
    char[] e = exp.toCharArray();
    //return isValid(s, e) ? process(s, e, 0, 0) : false;
    return isValid(s, e) && process(s, e, 0, 0);
}

//s[si...]能否被e[ei...]配出来
//必须保证ei压中的不是*
public static boolean process(char[] s, char[] e, int si, int ei){
    if (ei == e.length){ //base case : exp已经耗尽了
        return si == s.length;
    }
    //可能性一,ei+1位置, 不是*
    //str[si] 必须和exp[ei]配出来,并且后续能通
    if(ei + 1 == e.length || e[ei + 1] != '*'){
        return si != s.length && (e[ei] == s[si] || e[ei] == '.')
                    && process(s, e, si+1, ei+1);
    }
    //ei + 1位置是*
    while(si != s.length && (e[ei] == s[si] || e[ei] == '.')){
        if(process(s, e, si, ei + 2)){
            return ture;
        }
        si++;
    }
    return process(s, e, si, ei + 2);
}

改成DP

public static boolean isMatchDP(String str, String exp){
    if (str == null || exp == null){
        return false;
    }
    char[] s = str.toCharArray();
    char[] e = exp.toCharArray();
    if(!isValid(s, e)){
        return false;
    }
    boolean[][] dp = initDPMap(s, e);
    for(int i = s.length - 1; i > -1; i--){
        for(int j = e.length - 2; j > -1; j--){
            if(e[j + 1] != '*'){
                dp[i][j] = (s[i] == e[j] || e[j] == '.') && dp[i + 1][j + 1];
            }else{
                int si = i;
                while(si != s.length && (s[si] == e[j] || e[j] == '.')){
                    if(dp[si][j + 2]) {
                        dp[i][j] = true;
                        break;
                    }
                    si++;
                }
                if(dp[i][j] != true){
                    dp[i][j] = dp[si][j + 2];
                }
            }
        }
    }
    return dp[0][0];
}

public static boolean[][] initDPMap(char[] s, char[] e){
    int slen = s.length;
    int elen = e.length;
    boolean[][] dp = new boolean[slen + 1][elen + 1];
    dp[slen][elen] = true;
    for(int j = elen - 2; j > -1; j = j - 2){
        if(e[j] != '*' && e[j + 1] == '*'){
            dp[slen][j] = true;
        }else{
            break;
        }
    }
    if(slen > 0 && elen > 0){
        if ((e[elen - 1] == '.' || s[slen - 1] == e[elen - 1])){
            dp[slen - 1][elen - 1] = true'
        }
    }
    return dp;
}

递归:大问题调用小问题,

大问题做决策1调用小问题1  做决策2调用小问题2 ……

保证大问题做的所有的决策的影响都体现在小问题的参数上

(无后效性的递归)

数组二:数组异或和:数组中所有的数异或起来得到的值

给一个整型数组arr,有正有负有0,求子数组的最大异或和

经典做法:

public static int maxEOR(int[] arr){
    if(arr = null || arr.length == 0){
        return 0;
    }
    //尝试必须以arr[i]结尾的子数组,最大异或和是多少,尝试所有的arr[i]
    int ans = 0;
    for(int i = 0; i < arr.length; i++){
        //必须以arr[i]结尾
        //0..i每一个开头
        for(int start = 0; start <= i; start++){
            //arr[start...i]这个子数组
            int sum = 0;
            for(int index = start; index <= i; index++){
                sum ^= arr[index];
            }
        
            ans = Math.max(ans, sum);
        }
    }
    return ans;
}

省去遍历

public static int maxEOR2(int[] arr){
    if(arr = null || arr.length == 0){
        return 0;
    }
    //preSum[i] = arr[0..i]的异或和
    int[] preSum = new int[arr.length];
    preSum[0] = arr[0];
    for(int i = 1; i < arr.length; i++){
        preSum[i] = arr[i] ^ preSum[i - 1];
    }

    //尝试必须以arr[i]结尾的子数组,最大异或和是多少,尝试所有的arr[i]
    int ans = 0;
    for(int i = 0; i < arr.length; i++){
        //必须以arr[i]结尾
        //0..i每一个开头
        for(int start = 0; start <= i; start++){
            //arr[start...i]这个子数组
            int sum = preSum[i] ^ (start - 1 == -1 ? 0 : preSum[start - 1]);
            //arr[start..i]异或和 = arr[0..i] ^ arr[0..start - 1]
        
            ans = Math.max(ans, sum);
        }
    }
    return ans;
}

举例:

arr[11,        1,        15,        10,        13,        4]

      0          1           2          3             4        5

异或和:无 = 0 = 0000

        arr[0..0] = 11 = 1011

        arr[0..1] = 11 ^ 1 = 1010

        arr[0..2] = 0101

        arr[0..3] = 1111

        arr[0..4] = 0010

      sum = 0110

前缀树机制组织:

sum = 0110 

贪心策略:先迎合高位

ACJN 1011

public static class Node{
    public Node[] nexts = new Node[2];
}
//把所有前缀异或和,加入到NumTrie,并按照前缀树组织
public static class NumTrie{
    public Node head = new Node();
    
    public void add(int num){
        Node cur = head;
        for(int move = 31; move >= 0; move--){//move:向右移多少位
            int path = ((num >> move) & 1);
            cur.nexts[path] = cur.nexts[path] == null ? new Node() : cur.nexts[path];
            cur = cur.nexts[path];
        }
    }
    
    //sum最希望遇到的路径,最大的异或结果返回 O(32)
    public int maxXor(int sum){
        Node cur = head;
        int res = 0;//最后的结果(num ^ 最优选择)所得到的值
        for(int move = 31; move >= 0; move--){
            //当前位如果是0,path就是整数0
            //当前位如果是1,path就是整数1
            int path = (sum >> move) & 1; //num 第move位置上的状态提取出来
            //sum该位的状态,最期待的路
            int best = move == 31 ? path : (path ^ 1);
            //best : 最期待的路 -> 实际走的路
            best = cur.nexts[best] != null ? best : (best ^ 1);
            //path num第move位的状态,best是根据path实际走的路
            res |= (path ^ best) << move;
            cur = cur.nexts[best];
        }
        return res;
        
    }
}
public static int maxXorSubarray(int[] arr){
    if(arr == null || arr.length == 0){
        return 0;
    }
    int max =Integer.MIN_VALUE;
    int sum = 0; // 一个数也没有的时候,异或和为0
    NumTrie numTrie = new NumTrie();
    numTrie.add(0);
    for(int i = 0; i < arr.length; i++){
        sum ^= arr[i]; //sum -> 0 ~ i 异或和
        //numTrie 装着所有:一个数也没有、0~0、 0~1、0~2、 0~i-1
        max = Math.max(max, bumTrie.maxXor(sum));
        numTrie.add(sum);
    }
    return max;
}

题目三:打气球,leetcode 312

int f(L,R)尝试每个位置的气球都最后打爆

潜台词:L-1一定没爆,R+1一定没爆

public static int maxCoins1(int[] arr){
    if(arr = null || arr.length == 0){
        return 0;
    }
    if (arr.length == 1){
        return arr[0];
    }
    int N = arr.length;
    int[] help = new int[N + 2];
    help[0] = 1;
    help[N + 1] = 1;
    for(int i = 0; i < N; i++){
        help[i + 1] = arr[i];    
    }
    return process(help, 1, N);
}

//打爆arr[L..R]范围上的所有气球,返回最大的分数
//假设arr[L-1]和arr[R+1]一定没有被打爆
//尝试的方式:每一个位置的气球都最后被打爆
public static int process(int[] arr, int L, int R){
    if(L == R){//如果arr[L..R]范围上只有一个气球,直接打爆即可
        return arr[L - 1] * arr[L] * arr[R + 1];
    }
    //最后打爆arr[L]的方案,和最后打爆arr[R]的方案,先比较一下
    int max = Math.max(
        arr[L - 1] * arr[L] * arr[R + 1] + process(arr, L + 1, R),
        arr[L - 1] * arr[R] * arr[R + 1] + process(arr, L, R - 1, R));
    //尝试中间位置的气球最后被打爆的每一种方案
    for(int i = L + 1; i < R; i++){
        max = Math.max(max,
                    arr[L - 1] * arr[i] * arr[R + 1] + process(arr, L, i - 1)
                            + process(arr, i + 1, R)); 
    }
    return max;
}

N 层汉诺塔问题是2^N  - 1步

1)1~N-1 左 - 中

2)N 左 - 右 1步

3)1- N - 1 中-右

F(N) = 2F(N-1) + 1

i层的汉诺塔

from to other

1)1 ~ i-1 from - other

2)i from - to

3)1~ i - 1 other - to

public static int step1(int[] arr){
    if(arr == null || arr.length == 0){
        return -1;
    }
    return process(arr, arr.length - 1, 1, 2, 3);
}

//目标是:把arr[0~i]的圆盘,从from全部挪到to上
//返回,根据arr中的状态arr[0..i],它是最优解的第几步?
// O(N)
public static int process(int[] arr, int i, int from, int other, int to){
    if(i == -1){
        return 0;
    }
    if(arr[i] != from && arr[i] != to){
        return -1;
    }
    if(arr[i] == from){ //第一大步没走完
        return process(arr, i - 1, from, to, other);
    }else{ //arr[i] == to
        int rest = process(arr, i - 1, other, from, to);//第三大步完成的程度
        if(rest == -1){
            return -1;
        }
        return (1 << i) + rest;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值