第六十五天 --- 力扣-5868+5867+678
题目一
解题思路
1、栈
1、很经典很固定的思路,这种左右匹配的问题都可以尝试栈方法。
2、因为*有三个意思,所以在存储的时候,“(“单独一个栈, *单独一个栈
3、扫描的时候,优先匹配含义明确的,就是扫到“(“入栈,扫到 * 入栈,扫到“)“,优先匹配含义明确的“(“,等“(“都没了,再把匹配另一个栈,两个栈都空了,则肯定出错,因为右括号多了。
4、最难的问题来了,现在串扫描完了,如果左括号栈空了,则刚刚好,星号栈不用管,剩下的全当成是空串,返回true。当二者都不空时候,这里就要注意了,不是所有的星号都可以转化右括号将左括号栈里的元素抵消,二者有固定的相对位置要求,即左括号必须在右括号右面。所以我们需要存贮吗,每个字符的位置,栈里面如果存字符本身没用的,所以不存字符了,存位置,这样就知道相对位置信息了。(位置序号严格递增的)
5、最后目标就是用星号干掉剩下的左括号,比较之前看一下位置信息,不符合的话(即左括号位置大于所有星号位置),那么肯定不合法
2、区间DP
玩DP,先确定为什么是DP,再就是老四样,先找好几个状态,赋初始值,找转移,最后找答案。
确定算法
1、最简单的确定DP问题的方法就是找大小问题并且看二者有无转化方程式。
2、(( * ))对于这么一个玩意,大问题很显然,就是 (( * ))是否合法。字符串合不合法,要看左右括号能否匹配,所以小问题就是左右同时干掉一个即(*) ,所以当小问题合法,且大问题最左最右的符号匹配了,那么大问题也成立了,所以满足小问题推大问题,可以确定算法。
定状态
我们就是看当前区间的端点,以及区间内部小区间,所以仅用i j表示区间端点即可
找初始值(空串或者左一个右一个的字符)
括号匹配,要左边右边共同定,所以初始的时候,空串和 “*” 是固定的,以及 “()” ,"( * " , “ * )”,“**”是合法的,其余的都不合法,这就是初始值。
找转移
1、如果区间端点是相互匹配的,具体匹配看上一条列出的四种,那么大区间的结果完全和小区间一致
即dp[i][j] = dp[i + 1][j - 1];
2、当中间的部分不是合法的,但是因为*的存在所以当添加上两边的新点后,就有可能让整个串合法化,因为区间DP所以这个大串合不合法就要看被划分成的两个小串合法情况(这里这么想:是区间DP,小区间转化成为大区间,所以看大区间状态就只需要枚举小区间)
即dp[i][j] = dp[i][k] && dp[k + 1][j];
找答案
最终结果就是,看整个串的情况时候,他的结果,即dp[0][n - 1]
代码
1:栈
class Solution {
public:
stack<int> stack_char_one;//存储(
stack<int> stack_char_two;//存储*
bool checkValidString(string s) {
int num_s = s.size();
for (int i = 0; i < num_s; i++) {
//右括号固定对应左括号,*变化多,所以后处理
switch (s[i]) {
case '(':
stack_char_one.push(i);
break;
case '*':
stack_char_two.push(i);
break;
case ')':
if (stack_char_one.empty() && stack_char_two.empty()) {
return false;
}
else if (!stack_char_one.empty()) {
stack_char_one.pop();
}
else if (stack_char_one.empty() && !stack_char_two.empty()) {
stack_char_two.pop();
}
}
}
while (!stack_char_one.empty() && !stack_char_two.empty()) {
int one = stack_char_one.top();//取出左括号位置
stack_char_one.pop();
int two = stack_char_two.top();//取出右括号位置
stack_char_two.pop();
if (one >= two) {
//因为两个栈中的位置序号都是递增的,一旦栈头处左括号序号大,则星号的位置序号都会小于它,所以不合法了
return false;
}
}
if (!stack_char_one.empty()) {
return false;
}
return true;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N)
空间复杂度:O(N)
2:区间DP
注:
1、表示上述四种匹配情况语句:
(s[i] == '(' || s[i] == '*') && (s[i + 1] == ')' || s[i + 1] == '*')
2、
循环条件控制中,!dp[i][j],这里就是看dp[i][j]有无可能是true。一旦发现立即停止即可
class Solution {
public:
bool checkValidString(string s) {
int n = s.size();
if (s.empty()) {
return true;
}
bool dp[105][105] = {
false };//初始化
for (int i = 0; i < n; i++) {
//初始化
if (s[i] == '*') {
dp[i][i] = true;
}
}
for (int i = 0; i + 1 < n; i++) {
//初始化
dp[i][i + 1] = (s[i] == '(' || s[i] == '*') && (s[i + 1] == ')' || s[i + 1] == '*');
}
for (int l = 2; l < n; l++) {
for (int i = 0, j; i + l < n; i++) {
j = i + l;
if ((s[i] == '(' || s[i] == '*') && (s[j] == ')' || s[j] == '*')) {
dp[i][j] = dp[i + 1][j - 1];
}
for (int k = i; k < j && !dp[i][j]; k++) {
//区间DP,小区间转化成为大区间,所以看大区间状态就只需要枚举小区间
dp[i][j] = dp[i][k] && dp[k + 1][j];
}
}
}
return dp[0][n - 1];
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N^3)
空间复杂度:O(N^2)
题目二:
解题思路
直接模拟题目要求
代码
class Solution {
public:
string reversePrefix(string word, char ch) {
int i = 0;
string ans = "";
for (; i < word.size(); i++) {
//寻找目标位置
if (word[i] == ch) {
break;
}
}
if (i == word.size()) {
return word;//没找到
}
for (int j = i; j >= 0; j--) {
//做新串
ans += word[j];
}
for (int j = i + 1; j < word.size(); j++) {
ans += word[j];
}
return ans;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N)
题目三
思路:
1、按照要求,求出比值序列
2、一定注意,一个组数,一旦很混乱,则难以找规律,所以先排序,把一样的数放在一块
3、然后动态的找数字相同的有多少个,在用公式计算有多少种匹配方式即可。
代码:
注:
1、一定注意,题目中数据规模很大,并且他给的接口返回值long long,就知道他这里肯定数据量极大,所以全部int换成long long
class Solution {
public:
long long factorial(long long n) {
//匹配数目计算函数
return ((1 + n)*n) / 2;
}
long long interchangeableRectangles(vector<vector<int>>& rectangles) {
long long ans = 0;
long long n = rectangles.size();
vector<double> item;
for (long long i = 0; i < n; i++) {
item.push_back((rectangles[i][0] * 1.0) / rectangles[i][1]);//按照要求求
}
sort(item.begin(), item.end());//数据整理
long long cnt = 1;
double tmp = item[0];
for (long long i = 1; i < item.size(); i++) {
//动态查找
if (item[i] == tmp) {
cnt++;
}
else {
if (cnt <= 1) {
//点出现次数少于1,不会产生匹配
cnt = 1;
tmp = item[i];
}
else {
tmp = item[i];
ans += factorial(cnt - 1);
cnt = 1;
}
}
}
ans += factorial(cnt - 1);
return ans;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N)