题目一:判定一个由[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;
}
}