本文章的所有题目信息都来源于leetcode
如有侵权请联系我删掉!
时间:
今天的每日一题是:466. 统计重复个数
定义 str = [s, n] 表示 str 由 n 个字符串 s 连接构成。
例如,str == [“abc”, 3] ==“abcabcabc” 。
如果可以从 s2 中删除某些字符使其变为 s1,则称字符串 s1 可以从字符串 s2 获得。
例如,根据定义,s1 = “abc” 可以从 s2 = “abdbec” 获得,仅需要删除加粗且用斜体标识的字符。
现在给你两个字符串 s1 和 s2 和两个整数 n1 和 n2 。由此构造得到两个字符串,其中 str1 = [s1, n1]、str2 = [s2, n2] 。
请你找出一个最大整数 m ,以满足 str = [str2, m] 可以从 str1 获得。
示例 1:
输入:s1 = “acb”, n1 = 4, s2 = “ab”, n2 = 2
输出:2
示例 2:
输入:s1 = “acb”, n1 = 1, s2 = “acb”, n2 = 1
输出:1
好好好,三个月没写代码上来给一个hard题,行,我直接CV,写不了一点。
看了题解后发现其实就是寻找循环结:通俗一点就是在一个循环结中存在n1个s1中对应n2个s2,即寻找n1与n2的关系。而对于最后一个循环结可能是不完整的,所以对于最后一个循环结直接采用暴力匹配即可。
其次,这边不是很懂他这个循环结的寻找方式啊,到时候再看看题解。
class Solution {
public:
int getMaxRepetitions(string s1, int n1, string s2, int n2) {
if (n1 == 0) {
return 0;
}
int s1cnt = 0, index = 0, s2cnt = 0;
// recall 是我们用来找循环节的变量,它是一个哈希映射
// 我们如何找循环节?假设我们遍历了 s1cnt 个 s1,此时匹配到了第 s2cnt 个 s2 中的第 index 个字符
// 如果我们之前遍历了 s1cnt' 个 s1 时,匹配到的是第 s2cnt' 个 s2 中同样的第 index 个字符,那么就有循环节了
// 我们用 (s1cnt', s2cnt', index) 和 (s1cnt, s2cnt, index) 表示两次包含相同 index 的匹配结果
// 那么哈希映射中的键就是 index,值就是 (s1cnt', s2cnt') 这个二元组
// 循环节就是;
// - 前 s1cnt' 个 s1 包含了 s2cnt' 个 s2
// - 以后的每 (s1cnt - s1cnt') 个 s1 包含了 (s2cnt - s2cnt') 个 s2
// 那么还会剩下 (n1 - s1cnt') % (s1cnt - s1cnt') 个 s1, 我们对这些与 s2 进行暴力匹配
// 注意 s2 要从第 index 个字符开始匹配
unordered_map<int, pair<int, int>> recall;
pair<int, int> pre_loop, in_loop;
while (true) {
// 我们多遍历一个 s1,看看能不能找到循环节
++s1cnt;
for (char ch: s1) {
if (ch == s2[index]) {
index += 1;
if (index == s2.size()) {
++s2cnt;
index = 0;
}
}
}
// 还没有找到循环节,所有的 s1 就用完了
if (s1cnt == n1) {
return s2cnt / n2;
}
// 出现了之前的 index,表示找到了循环节
if (recall.count(index)) {
auto [s1cnt_prime, s2cnt_prime] = recall[index];
// 前 s1cnt' 个 s1 包含了 s2cnt' 个 s2
pre_loop = {s1cnt_prime, s2cnt_prime};
// 以后的每 (s1cnt - s1cnt') 个 s1 包含了 (s2cnt - s2cnt') 个 s2
in_loop = {s1cnt - s1cnt_prime, s2cnt - s2cnt_prime};
break;
} else {
recall[index] = {s1cnt, s2cnt};
}
}
// ans 存储的是 S1 包含的 s2 的数量,考虑的之前的 pre_loop 和 in_loop
int ans = pre_loop.second + (n1 - pre_loop.first) / in_loop.first * in_loop.second;
// S1 的末尾还剩下一些 s1,我们暴力进行匹配
int rest = (n1 - pre_loop.first) % in_loop.first;
for (int i = 0; i < rest; ++i) {
for (char ch: s1) {
if (ch == s2[index]) {
++index;
if (index == s2.size()) {
++ans;
index = 0;
}
}
}
}
// S1 包含 ans 个 s2,那么就包含 ans / n2 个 S2
return ans / n2;
}
};
没啥收获,太难了,仔细看了下题解能看懂,希望过几天能回来复现一下!
本题来自动态规划基础篇之——70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
方法一:一般直接递归,递推函数F(n)=F(n-1)+F(n-2),(n>=2),其中F(1)=1,F(2)=2。F(n)表示第n阶台阶有多少种方式走上去,而根据只能通过走一阶和走两阶这二中方式才行,所以得出递推公式。(递归也可以,递推也行)
方法二:矩阵快速幂
我就不多说了,去看下题解,解的很巧,一般用于其次方程组,非齐次也可以转为为齐次再用这个方法。
方法三:通项公式
计算机的尽头是数学!
递推:
class Solution {
public:
int climbStairs(int n) {
if(n==1)
return 1;
if(n==2)
return 2;
int a1=1;
int a2=2;
int ans=0;
for(int i=3;i<=n;i++)
{
ans=a1+a2;
a1=a2;
a2=ans;
}
return ans;
}
};
矩阵快速幂:(转载自力扣官方题解)
class Solution {
public:
vector<vector<long long>> multiply(vector<vector<long long>> &a, vector<vector<long long>> &b) {
vector<vector<long long>> c(2, vector<long long>(2));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
vector<vector<long long>> matrixPow(vector<vector<long long>> a, int n) {
vector<vector<long long>> ret = {{1, 0}, {0, 1}};
while (n > 0) {
if ((n & 1) == 1) {
ret = multiply(ret, a);
}
n >>= 1;
a = multiply(a, a);
}
return ret;
}
int climbStairs(int n) {
vector<vector<long long>> ret = {{1, 1}, {1, 0}};
vector<vector<long long>> res = matrixPow(ret, n);
return res[0][0];
}
};
矩阵快速幂这种方式十分的快,在解决某些有规律的递推公式上有着十分迅速的解决方式以及是分小的开销。