左神:高级进阶班6

1.匹配字符串

2.最大异或和子数组

3.打气球得分

4.判断汉诺塔轨迹是最优解第几步​编辑


1.匹配字符串

思路:
    1. 递归函数bool f(string str,string exp,int si,int ei)
    2. bool f(string str,string exp,int si,int ei):str[si...]和exp[ei...]是否能匹配
      关注ei+1位置上的符号,因为存在'*',可以改变其前面的字符
      2.1 exp[ei+1]!='*':exp[ei]==str[si]||exp[ei]=='.'
      2.2 exp[ei+1]=='*':看*换算成几个e[ei]合适

bool isValid(string s, string 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;
}

//s[si...]能否被e[ei...]配出来
//必须保证ei压中的不是'*'
bool process(string s, string e, int si, int ei) {
	if (ei == e.length()) {//e已经走完,只有s也走完了才能匹配
		return si == s.length();
	}
	//可能性1,ei+1位置,不是'*'
	if (ei + 1 == e.length() || e[ei + 1] != '*') {//str[si]必须和exp[ei]配出来,并且后续也能匹配
		return si != s.length() && (e[ei] == s[si] || e[ei] == '.') && process(s, e, si + 1, ei + 1);
	}
	//可能性2,ei+1位置,是'*'
	while (si != s.length() && (s[si] == e[ei]) || e[ei] == '.') {
		if (process(s, e, si, ei + 2)) {
			return true;
		}
		si++;
	}
	//如果s[si] != e[ei],直接跳过while循环,0个e[ei]
	return process(s, e, si, ei + 2);
}

vector<vector<bool>>initDPMap(string s, string e) {
	int slen = s.length();
	int elen = e.length();
	vector<vector<bool>>dp(slen + 1, vector<bool>(elen + 1, false));
	dp[slen][elen] = true;
	for (int j = elen - 2; j > -1; 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;
}

//递归改动态规划,由于递归过程中exp不会压到*,
//因此二维表中的格子也不考虑当前位是*的情况,因为用不到
//dp[si][ei]:str[si...]能否和exp[ei...]匹配
bool isMatchDp(string s, string e) {
	if (!isValid(s, e)) {
		return false;
	}
	vector<vector<bool>>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];
}

2.最大异或和子数组

   arr[0...i]=arr[0...(start-1)]^arr[start...i]
<=>arr[start...i]=arr[0...i]^arr[0...(start-1)]

class Node {
public:
	vector<Node*>nexts;
	Node() {
		nexts = vector<Node*>(2);
	}
};

//把所有前缀异或和加入到NumTrie中,并按照前缀树组织
class NumTrie {
public:
	Node* head=NULL;

	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最希望遇到的路径,最大的异或和结果返回
	int maxXor(int sum) {
		Node* cur = head;
		int res = 0;//最后的结果(sum^最优选择)所得到的值
		for (int move = 31; move >= 0; move--) {
			//当前位如果是0,path就是整数0
			//当前位如果是1,path就是整数1
			int path = (sum >> move) & 1;//sum第move位置上的状态提取出来
			//sum该位的状态,最期待的路
			int best = move == 31 ? path : (path ^ 1);
			//best:最期待的路 -> 实际走的路
			best = cur->nexts[best] != NULL ? best : (best ^ 1);
			//path sum第move位的状态,best是根据path实际走的路
			res |= (path ^ best) << move;
			cur = cur->nexts[best];
		}
		return res;
	}
};

int maxXorSubarray(vector<int>arr) {
	if (arr.size() == 0) {
		return 0;
	}
	int maxXor = INT_MIN;
	int sum = 0;//一个数都没有的时候,异或和为0
	NumTrie numTrie;
	numTrie.add(0);
	for (int i = 0; i < arr.size(); i++) {
		sum ^= arr[i];//[0...i]异或和
		//前缀树numTrie装着所有:一个数也没有、0~0、0~1、0~2、0~i-1的异或和
		maxXor = max(maxXor, numTrie.maxXor(sum));
		numTrie.add(sum);
	}
	return maxXor;
}

3.打气球得分

//打爆arr[L..R]范围上的	所有气球,返回最大分数
//假设arr[L-1]和arr[R+1]一定没有被打爆
//尝试的方式:每一个位置的气球都是最后被打爆
int process(vector<int>& arr, int L, int R) {
	if (L == R) {//只剩一个气球,直接打爆
		return arr[L - 1] * arr[L] * arr[R + 1];
	}
	//比较最后打爆arr[L]和最后打爆arr[R]两个方案
	int maxIntegral = 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));
	//尝试中间位置的气球最后被打爆的每一种方案
	for (int i = L + 1; i < R; i++) {
		maxIntegral = max(maxIntegral, arr[L - 1] * arr[i] * arr[R + 1] + process(arr, L, i - 1) + process(arr, i + 1, R));
	}
	return maxIntegral;
}

int maxIntegral(vector<int>& arr) {
	if (arr.size() == 0) {
		return 0;
	}
	if (arr.size() == 1) {
		return arr[0];
	}
	int N = arr.size();
	vector<int>help(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);
}

//递归改动态规划
int maxIntegralDp(vector<int>& arr) {
	if (arr.size() == 0) {
		return 0;
	}
	if (arr.size() == 1) {
		return arr[0];
	}
	int N = arr.size();
	vector<int>help(N + 2);
	help[0] = 1;
	help[N + 1] = 1;
	for (int i = 0; i < N; i++) {
		help[i + 1] = arr[i];
	}
	vector<vector<int>>dp(N + 2, vector<int>(N + 2));
	for (int i = 1; i <= N; i++) {
		dp[i][i] = help[i - 1] * help[i] * help[i + 1];
	}
	for (int L = N; L >= 1; L--) {
		for (int R = L + 1; R <= N; R++) {
			// 求解dp[L][R],表示help[L..R]上打爆所有气球的最大分数
			// 最后打爆help[L]的方案
			int finalL = help[L - 1] * help[L] * help[R + 1] + dp[L + 1][R];
			// 最后打爆help[R]的方案
			int finalR = help[L - 1] * help[R] * help[R + 1] + dp[L][R - 1];
			// 最后打爆help[L]的方案,和最后打爆help[R]的方案,先比较一下
			dp[L][R] = max(finalL, finalR);
			// 尝试中间位置的气球最后被打爆的每一种方案
			for (int i = L + 1; i < R; i++) {
				dp[L][R] = max(dp[L][R], help[L - 1] * help[i]
					* help[R + 1] + dp[L][i - 1] + dp[i + 1][R]);
			}
		}
	}
	return dp[1][N];
}

4.判断汉诺塔轨迹是最优解第几步

//目标:把0~i的圆盘,从from全部挪到to上
//返回:根据arr中的状态arr[0...i],它是最优解第几步
int process(vector<int>& arr, int i, int from, int other, int to) {
	if (i == -1) {
		return 0;
	}
	if (arr[i] != from && arr[i] != to) {//i圆盘没有到other的轨迹
		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;
	}
}

int step(vector<int>& arr) {
	if (arr.size() == 0) {
		return -1;
	}
	return process(arr, arr.size() - 1, 1, 2, 3);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jomo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值