左神:高级进阶班7

1.互为旋变串

2.str1子串中含有str2所有字符的最小子串长度

3.LFU缓存

4.良好出发点


1.互为旋变串

 

第一种情况:a1未和a2~a5交换,a1对应b1

第二种情况:a1和a2~a5交换,a1对应b5  

//判断字符串s1和s2的长度还有各字符词频是否相同
bool sameTypeSameNumber(string s1, string s2) {
	if (s1.length() != s2.length()) {
		return false;
	}
	vector<int>times(256);
	for (int i = 0; i < s1.length(); i++) {
		times[s1[i]]++;
	}
	for (int i = 0; i < s2.length(); i++) {
		if (--times[s2[i]] < 0) {
			return false;
		}
	}
	return true;
}

//判断str1(L1,L1+size)和str2(L2,L2+size)是否为旋变串
//长度一定相等
bool process(string str1, string str2, int L1, int L2, int size) {
	if(size==1){//base case
		return str1[L1] == str2[L2];//长度等于1时,两字符相等即互为旋变串
	}

	for (int leftPart = 1; leftPart < size; leftPart++) {
		//str1:左1右1
		//str2:左2右2
		if (
			process(str1, str2, L1, L2, leftPart)//左1对左2(a1<=>b1)
			&&
			process(str1, str2, L1 + leftPart, L2 + leftPart, size - leftPart)//右1对右2(a2~a5<=>b2~b5)
			||
			process(str1, str2, L1, L2 + size - leftPart, leftPart)//左1对右2(a1<=>b5)
			&&
			process(str1, str2, L1 + leftPart, L2, size - leftPart)//右2对左1(a2~a5<=>b1~b4)
		   )
		{
			return true;
		}
	}
	return false;
}


bool isScramble(string s1, string s2) {
	if (s1.length() == 0 || s2.length() == 0) {
		return s1.length() == s2.length();
	}
	if (s1 == s2) {
		return true;
	}
	if (!sameTypeSameNumber(s1, s2)) {
		return false;
	}
	int N = s1.length();
	return process(s1, s2, 0, 0, N);
}

//递归改动态规划,位置依赖:看第三围参数,leftPart总是比size小
//三位表就是一个正方体,边长为字符串长度
//注意base case是size==1,因此size没有第0层
//保证str1和str2是等长同类型的
bool dpCheck(string str1, string str2) {
	int N = str1.length();
	vector<vector<vector<bool>>>dp(N, vector<vector<bool>>(N, vector<bool>(N + 1)));
	//先把第一层填了,base case
	for (int L1 = 0; L1 < N; L1++) {
		for (int L2 = 0; L2 < N; L2++) {
			dp[L1][L2][1] = str1[L1] == str2[L2];
		}
	}
	//按层来填,因此size在for循环最外层
	for (int size = 2; size <= N; size++) {
		for (int L1 = 0; L1 <= N - size; L1++) {//注意出发点加上长度的位置不能超过字符串总长度
			for (int L2 = 0; L2 <= N - size; L2++) {
				for (int leftPart = 1; leftPart < size; leftPart++) {
					if (
						dp[L1][L2][leftPart]
						&&
						dp[L1 + leftPart][L2 + leftPart][size - leftPart]
						||
						dp[L1][L2 + leftPart][leftPart]
						&&
						dp[L1 + leftPart][L2][size - leftPart]
						) 
					{
						dp[L1][L2][size] = true;//该层该位置有满足条件的情况
						break;
					}
				}
			}
		}
	}
	return dp[0][0][N];
}

2.str1子串中含有str2所有字符的最小子串长度

int minLen(string str1, string str2) {
	if (str1.length() == 0 || str2.length() == 0) {
		return 0;
	}
	vector<int>needs(256);
	for (int i = 0; i < str2.length(); i++) {
		needs[str2[i]]++;
	}

	//移动窗口扫描str1子串
	int left = 0;
	int right = 0;
	int match = str2.length();
	int minLen = INT_MAX;
	while (right < str1.length()) {
		needs[str1[right]]--;
		if (needs[str1[right]] >= 0) {//欠的是从正数减1的话才是有效的偿还
			match--;
		}
		if (match == 0) {
			while (needs[str1[left]] < 0) {//在以right结尾的情况下,右移left,看是否能缩减子串长度
				needs[str1[left++]]++;//既然窗口不包含str1[left]了,那么又多欠一个该字符了
			}
			minLen = min(minLen, right - left + 1);
			//退出while循环时,needs[str1[left]]==0,计算完长度后,右移left进行下次循环
			needs[str1[left++]]++;
			match++;//从非负数加1,match才能有效的加1
		}
		right++;
	}
	return minLen;
}

3.LFU缓存

//节点的数据结构
class Node {
public:
	int key;
	int value;
	int times;//该节点发生get和set的次数总和
	Node* up;//节点之间是双向链表所以有上一个节点
	Node* down;//节点之间是双线链表所以有下一个节点

	Node(int key, int value, int times) {
		this->key = key;
		this->value = value;
		this->times = times;
		this->up = NULL;
		this->down = NULL;
	}
};

//桶结构
class NodeList {
public:
	Node* head;//桶的头节点
	Node* tail;//桶的尾节点
	NodeList* last;//桶之间是双向链表所以有前一个桶
	NodeList* next;//桶之间是双向链表所以有后一个桶

	NodeList(Node* node) {
		this->head = node;
		this->tail = node;
	}

	//把一个新的节点加入到这个桶,新的节点放在顶端变成新的头部
	void addNodeFromHead(Node* newHead) {
		newHead->down = head;
		head->up = newHead;
		head = newHead;
	}

	//判断这个桶是不是空的
	bool isEmpty() {
		return head == NULL;
	}

	//删除node节点并保证node的上下环境重新链接
	void deleteNode(Node* node) {
		if (head == NULL) {
			head = NULL;
			tail = NULL;
		}
		else {
			if (node == head) {
				head = node->down;
			}
			else if (node == tail) {
				tail = tail->up;
				tail->down = NULL;
			}
			else {
				node->up->down = node->down;
				node->down->up = node->up;
			}
		}
		node->up = NULL;
		node->down = NULL;
	}
};

//总缓存结构
class LFUCache {
public:
	int capacity;//缓存大小限制k
	int size;//缓存目前有多少个节点
	map<int, Node*>records;//表示key由哪个节点代表
	map<Node*, NodeList*>heads;//表示节点在哪个桶里
	NodeList* headList;//整个结构中最左的桶

	LFUCache(int K) {
		capacity = K;
		size = 0;
		headList = NULL;
	}

	/*
	removeNodeList:刚刚减少了一个节点的桶
	这个函数的功能是:判断刚刚减少了一个节点的桶是不是已经空了
	1)如果不空,什么也不做
	2)如果空了,并且removeNodeList是整个缓存结构最左的桶(headList)
	   删掉这个桶的同时也要让removeNodeLIst的下一个桶变成最左的桶
	3)如果空了,但removeNodeList不是整个缓存结构最左的桶(headList)
	   把这个桶删除,并保证上一个的桶和下一个的桶还是双向链表的连接方式
	*/
	bool modifyHeadList(NodeList* removeNodeList) {
		if (removeNodeList->isEmpty()) {
			if (headList == removeNodeList) {
				headList = removeNodeList->next;
				if (headList != NULL) {
					headList->last == NULL;
				}
			}
			else {
				removeNodeList->last->next = removeNodeList->next;
				if (removeNodeList->next != NULL) {
					removeNodeList->next->last = removeNodeList->last;
				}
			}
			return true;
		}
		return false;
	}

	/*
	函数的功能:
	node节点次数+1了,但该节点原来在oldNodeList里
	把node从oldNodeList删掉,然后放到次数+1的桶里
	整个保证既要保证节点之间的双向链表关系,同时也要保证桶之间的双向链表关系
	*/
	void move(Node* node, NodeList* oldNodeList) {
		//preList表示次数+1的桶前一个桶是哪一个
		//如果oldNodeList删掉node之后还有节点,oldNodeList就是次数+1的桶的前一个桶
		//如果oldNodeList删掉node之后空了,oldNodeList是需要删除的,所以次数+1的桶的前一个桶,是oldNodeList的前一个
		NodeList* preList = modifyHeadList(oldNodeList) ? oldNodeList->last : oldNodeList;
		//nextList表示次数+1的桶的后一个桶是哪一个
		NodeList* nextList = oldNodeList->next;
		if (nextList == NULL) {
			NodeList* newList = new NodeList(node);
			if (preList != NULL) {
				preList->next = newList;
			}
			newList->last = preList;
			if (headList == NULL) {
				headList = newList;
			}
			heads[node] = newList;
		}
		else {
			if (nextList->head->times == node->times) {
				nextList->addNodeFromHead(node);
				heads[node] = nextList;
			}
			else {
				NodeList* newList = new NodeList(node);
				if (preList != NULL) {
					preList->next = newList;
				}
				newList->last = preList;
				newList->next = nextList;
				nextList->last = newList;
				if (nextList==headList) {
					headList = newList;
				}
				heads[node] = newList;
			}
		}
	}

	void set(int key, int value) {
		if (records.count(key) != 0) {
			Node* node = records[key];
			node->value = value;
			node->times++;
			NodeList* curNodeList = heads[node];
		}
		else {
			if (size == capacity) {
				Node* node = headList->tail;
				headList->deleteNode(node);
				modifyHeadList(headList);
				records.erase(node->key);
				heads.erase(node);
				size--;
			}
			Node* node = new Node(key, value, 1);
			if (headList == NULL) {
				headList = new NodeList(node);
			}
			else {
				if (headList->head->times == node->times) {
					headList->addNodeFromHead(node);
				}
				else {
					NodeList* newList = new NodeList(node);
					newList->next = headList;
					headList->last = newList;
					headList = newList;
				}
			}
			records[key] = node;
			heads[node] = headList;
			size++;
		}
	}

	int get(int key) {
		if (records.count(key) == 0) {
			_Throw_range_error("无效key");
		}
		Node* node = records[key];
		node->times++;
		NodeList* curNodeList = heads[node];
		move(node, curNodeList);
		return node->value;
	}
};

4.良好出发点

思路:
    纯能数组=oil-dis,oil-=dis,用完再复原,问题转变成在纯能数组中找一个出发点,过程中累加和不能小于0
    

int changeDisArrayGetInit(vector<int>& dis, vector<int>& oil) {
	int init = -1;
	for (int i = 0; i < dis.size(); i++) {
		dis[i] = oil[i] - dis[i];
		if (dis[i] >= 0) {
			init = i;
		}
	}
	return init;
}

int lastIndex(int index, int size) {
	return index == 0 ? (size - 1) : index - 1;
}

int nextIndex(int index, int size) {
	return index == size - 1 ? 0 : index + 1;
}

//已知start的next方向上有一个良好出发点
//start如果可以达到这个良好出发点,	那么从start出发一定可以转一圈
void connectGood(vector<int>& dis, int start, int init, vector<bool>& res) {
	int need = 0;
	while (start != init) {
		if (dis[start] < need) {
			need -= dis[start];
		}
		else {
			res[start] = true;
			need = 0;
		}
		start = lastIndex(start, dis.size());
	}
}

vector<bool>enlargeArea(vector<int>& dis, int init) {
	vector<bool>res(dis.size());
	int start = init;
	int end = nextIndex(init, dis.size());
	int need = 0;
	int rest = 0;
	do {
		//当前来到的start已经在连通区域中, 可以确定后续的开始点一定无法转完一圈
		if (start != init && start == lastIndex(end, dis.size())) {
			break;
		}
		//当前的start不在连通区域中,就扩充连通区域
		if (dis[start] < need) {
			need -= dis[start];
		}
		else {
			rest += dis[start] - need;
			need = 0;
			while (rest >= 0 && end != start) {
				rest += dis[end];
				end = nextIndex(end, dis.size());
			}
			//如果连通区域已经覆盖整个环,当前的start是良好出发点,进入2阶段
			if (rest >= 0) {
				res[start] = true;
				connectGood(dis, lastIndex(start, dis.size()), init, res);
				break;
			}
		}
		start = lastIndex(start, dis.size());
	} while (start != init);
	return res;
}

vector<bool>stations(vector<int>& dis, vector<int>& oil) {
	if (dis.size() != oil.size() || dis.size() < 2) {
		return {};
	}
	int init = changeDisArrayGetInit(dis, oil);
	return init == -1 ? vector<bool>(dis.size(), false) : enlargeArea(dis, init);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jomo.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值