今天我们将在豆包MarsCode AI刷题平台上,完成《超级有效子字符串计数》与《端点石子移动问题》这两个算法问题,通过这些练习提升用户解决此类问题的能力
《超级有效子字符串计数》题面如下:
问题理解
题目要求我们计算一个字符串中包含的超级有效子字符串的数量。超级有效子字符串是由不同的有效字符串连接而成的字符串。有效字符串的定义是:由 X 个 < 开头,接着一个 |,最后是 X 个 > 组成的字符串。
数据结构选择
我们选择了两个数组 preleftnum 和 prerightnum 来分别记录每个位置左侧连续 < 的数量和右侧连续 > 的数量。
算法步骤
-
预处理左侧连续
<的数量:- 遍历字符串,如果当前字符是
<,则preleftnum[i]记录从当前位置向左连续<的数量。 - 如果当前字符不是
<,则preleftnum[i]置为 0。
- 遍历字符串,如果当前字符是
-
预处理右侧连续
>的数量:- 由右向左遍历字符串,如果当前字符是
>,则prerightnum[i]记录由当前位置向右连续>的数量。 - 如果当前字符不是
>,则prerightnum[i]置为 0。
- 由右向左遍历字符串,如果当前字符是
-
计算超级有效子字符串的数量:
- 遍历字符串,找到每个
|的位置。 - 对于每个
|,计算其左侧连续<的数量leftnum和右侧连续>的数量rightnum。 - 计算
leftnum和rightnum的最小值,并累加到结果ret中。 - 如果
leftnum和rightnum相等且大于 0,则说明当前|可以与之前的|形成更多的超级有效子字符串,累加pre的值到ret,并更新pre。
- 遍历字符串,找到每个
具体实现
int solution(int N, const std::string& S) {
// write code here
int ret=0;
int pre=0;
vector<int> preleftnum(N,0), prerightnum(N,0);
// 预处理左侧连续 < 的数量
for(int i=0;i<N;i++){
if(S[i] == '<'){
preleftnum[i] = i-1>=0 ? preleftnum[i-1]+1 : 1;
} else {
preleftnum[i] = 0;
}
}
// 预处理右侧连续 > 的数量
for(int i=N-1;i>=0;i--){
if(S[i] == '>'){
prerightnum[i] = i+1<N ? prerightnum[i+1]+1 : 1;
} else {
prerightnum[i] = 0;
}
}
// 计算超级有效子字符串的数量
for(int i=0;i<N;i++){
if(S[i] == '|'){
int leftnum=i-1>=0 ? preleftnum[i-1] : 0, rightnum=i+1<N ? prerightnum[i+1] : 0;
ret += min(leftnum, rightnum);
if(leftnum == rightnum && leftnum>0){
ret += pre;
pre++;
} else {
pre=0;
}
}
}
return ret;
}
-
时间复杂度:
O(N) -
空间复杂度:
O(N) -
其中
N是字符串的长度。
《端点石子移动问题》题面如下:
问题理解
题目要求我们计算将所有端点石子移动到 非端点位置 所需的最小次数。端点石子是指位于数组 stones 中最小或最大位置的石子。如果石子的位置是连续的,则游戏结束,因为没有可以进行的移动操作。
数据结构选择
- 由于我们需要对石子的位置进行排序和查找操作,因此选择使用
vector来存储石子的位置。 - 首先对
stones进行排序,以便后续处理。
算法步骤
- 排序:首先对
stones进行排序,这样我们可以更容易地找到端点石子。 - 计算移动次数:遍历排序后的
stones,计算每个位置作为端点石子时,需要移动的次数。 - 特殊情况处理:在遍历过程中,因为要求端点石子移动之后要位于非端点位置,所以要对特殊情况进行处理。
具体实现
int solution(vector<int>& stones) {
int n = stones.size();
int ret = n;
sort(stones.begin(), stones.end());
for(int i = 0; i < n; i++) {
// 计算由当前位置 stones[i] 开始,有多少个石子在 stones[i] + n 之后
int rightNum = distance(lower_bound(stones.begin(), stones.end(), stones[i] + n), stones.end());
// 计算当前位置作为左端点石子时,需要移动的次数,i指的是有多少个石子在 stones[i] 之前
int moveNum = i + rightNum;
// 特殊情况处理
// 如果当前位置是第一个石子,并且只有一个石子在 stones[i] + n 之后,则需要移动两次
if(i == 0 && rightNum == 1) {
moveNum = 2;
}
// 如果当前位置是第二个石子,并且后面所有石子都是连续的,则需要移动两次
if(i == 1 && stones[n-1] - stones[1] + 1 == n - 1) {
moveNum = 2;
}
ret = min(ret, moveNum);
}
return ret;
}
借助豆包MarsCode AI刷题平台,我们不仅高效地解决了《超级有效子字符串计数》和《端点石子移动问题》,还加深了对相关算法和数据结构的理解,后续会借助豆包MarsCode AI给大家展示更多题目的解法。