知识点:模拟
先创建一个等长的字符串,然后按顺序对其更新即可。
class Solution {
public:
string restoreString(string s, vector<int>& indices) {
string str = s;
for(int i = 0; i < s.size(); i++) {
str[indices[i]] = s[i];
}
return str;
}
};
知识点:思维题
首先找到第一个和目标状态不一样的灯泡,然后改变其和其后所有灯泡的状态。然后重复前述步骤,直到所有灯泡都到达目标状态。
接下来用我拙劣的语言来说明一下该算法是收敛的:
虽然每次操作可能会让后面的灯泡不符合目标状态,但是可以保证当前被选中的灯泡变成目标状态的且不会反复。所以可以保证最终所有灯泡都会变成合法的。
再用我拙劣的的语言来说明下这样做是最优的:
设第一个不合法的灯泡的下标为 i,那么必须对 i 进行操作。原因如下:
综上,必须要操作 i。当对 i 操作之后,要么全都达到了目标状态,要么产生了一个新的 i。也就是说对于每一种初始状态,其中要被操作的那些灯泡(姑且称之为关键灯泡)就已经被确定了。无论你按什么样的次序操作,这些关键灯泡都是要操作的,而对其他灯泡的操作都是冗余的(如原因二所述),所以这样操作是最优的。
class Solution {
public:
int minFlips(string target) {
int anw = 0;
bool status = false;
for(int i = 0; i < target.size(); i++) {
if(target[i] == '0') {
if(status == false) {
continue;
} else {
anw ++;
status = false;
}
} else {
if(status == true) {
continue;
} else {
anw ++;
status = true;
}
}
}
return anw;
}
};
知识点:深度优先遍历
先来说个前提:二叉树中,两个节点的最近路径是唯一的,且必然会经过两个节点的最近公共祖先。
最近公共祖先(LCA):设有节点F,A,B,如果F即是A的祖先,又是B的祖先,那么F是A,B的公共祖先。在所有符合上述要求的F中,深度最大的那个即为A,B的最近公共祖先。
设以节点 F 为LCA的好叶子节点对为 C(F)。那么最终的答案即为所有C(F)的累加和。那如何求解C(F)呢?
首先分别左右子树中到F的距离为 x(1 <= x <= distance) 的叶子节点的数量。
然后将枚举距离将左右子树的叶子节点配对,统计其中符合距离要求的点对即可。
拙劣的 证明:
因为每对叶子节点的LCA是唯一的,且LCA必定存在,所以通过LCA来区分点对既不会重复也不会漏掉。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
// cntDict::key 是节点的内存地址,用作一个节点的id。
// cntDict::value 也是一个 map, 其 value 为到 cntDict::key 距离为 key 的数量。
map<ptrdiff_t, map<int, int>> cntDict;
int dfs(TreeNode *root, int distance) {
if(root == nullptr) {
return 0;
}
int anw = 0;
anw += dfs(root->left, distance); // 累加右子树中点对的数量
anw += dfs(root->right, distance); // 累加左子树中点对的数量
//左右节点都存在,那么必有以 root 为LCA 的点对,统计其中符合要求的叶子点对数量。
if(root->left != nullptr && root->right != nullptr) {
const map<int, int> &left = cntDict[ptrdiff_t(root->left)];
const map<int, int> &right = cntDict[ptrdiff_t(root->right)];
for(int i = 0; i < distance; i++) {
auto lit = left.find(i);
if(lit == left.end()) { continue; }
for(int j = 0; j < distance; j++) {
if(i+j+2 > distance) {
continue;
}
auto rit = right.find(j);
if(rit == right.end()) {
continue;
}
anw += lit->second * rit->second;
}
}
}
map<int, int> cnt;
if(root->left != nullptr) {
const map<int, int> &left = cntDict[ptrdiff_t(root->left)];
for(auto it = left.begin(); it != left.end(); ++it) {
if(it->first + 1 >= distance) { continue; }
cnt[it->first+1] += it->second;
}
}
if(root->right != nullptr) {
const map<int, int> &left = cntDict[ptrdiff_t(root->right)];
for(auto it = left.begin(); it != left.end(); ++it) {
if(it->first + 1 >= distance) { continue; }
cnt[it->first+1] += it->second;
}
}
if(root->left == nullptr && root->right == nullptr) {
cnt[0] = 1;
}
cntDict.insert(make_pair(ptrdiff_t(root), cnt));
return anw;
}
int countPairs(TreeNode* root, int distance) {
return dfs(root, distance);
}
};
知识点:动态规划
这个题写的相当挫。。
既然是动态规划,那么先来确定状态:
设 s 的长度为n,下标从 1 开始。
f(i,j,d) 为以 s[i] 结尾,且末尾有 j 个连续的 s[i],且删了 d 个字符的最小长度。
当我们向 s 末尾追加一个 s 中不存在的字符(比如 #),那么答案就是 f(n+1, 1, k) - 1。减一是为了移除追加的特殊字符。
状态确定了,再来看如何转移:
先把状态摆在这里:
class Solution {
public:
int dp[102][101][101];
int get(int con) {
// con == 1, 说明要从 x 变为 x2
// con == 9, 说明要从 x9 变为 x10
// con == 9, 说明要从 x99 变为 x100
return (con == 1 || con == 9 || con == 99) ? 1 : 0;
}
int getLengthOfOptimalCompression(string s, int k) {
if(k >= s.size()) {
return 0;
}
s += "#";
memset(dp, 0x7f, sizeof(dp));
dp[0][0][0] = 0;
// 枚举最后一个字符的下标。
for(int now = 1; now <= s.size(); now++) {
// 枚举 now 之前保留的最后一个字符 pre。
for(int pre = now-1; pre >= 0; pre--) {
if(now-pre-1 > k) {
break;
}
// 枚举 pre 之前删除了 deleted 个字符。
for(int deleted = 0; deleted <= k; deleted++) {
int allDeleted = deleted + now-pre-1;
if(allDeleted > k) {
break;
}
// 枚举连续的 s[pre] 的个数
for(int con = 0; con <= pre; con++) {
if(dp[pre][con][deleted] > 100) {
// 大于 100 说明该状态不可达。
continue;
}
// 转移
if(pre != 0 && s[pre-1] == s[now-1]) {
dp[now][con+1][allDeleted] = min(dp[now][con+1][allDeleted], dp[pre][con][deleted] + get(con));
} else {
dp[now][1][allDeleted] = min(dp[now][1][allDeleted], dp[pre][con][deleted] + 1);
}
}
}
}
}
return dp[s.size()][1][k] - 1;
}
};