【筆でリートコード60日】day35——452.風船を爆発させる矢の数は最小限にする、435.間隔は重複しない、763.文字間隔を分ける


  トピック:

452. 最も少ない矢の数で風船を爆発させる

XY 平面で表される壁に球形のバルーンが取り付けられています。points 壁上のバルーンは、 と の間の水平直径を持つバルーンpoints[i] = [xstart, xend] を表す 整数の配列で記録されます 。バルーンの正確な y 座標はわかりません。xstartxend

弓矢は、X 軸に沿ったさまざまな点から 完全に垂直に射ることができます 。座標に 矢印を放ち、その座標で始まり、その座標で終わる直径を満足する  x 風船があれば 、その風船は爆発します  。射出できる弓の数 に制限はありません 。弓矢は一度放たれると無限に前進することができます。xstartxendxstart ≤ x ≤ xend

配列を指定して points 、すべての風船を破裂させるために発射する必要がある  矢の 最小数を返します。

例 1:

入力:ポイント = [[10,16],[2,8],[1,6],[7,12]]
出力: 2
説明:風船は 2 本の矢印で破裂できます。
- x = 6 に矢を放ち、風船 [2,8] と [1,6] を割ります。
- x = 11 に矢を放ち、風船 [10,16] と [7,12] を割ります。

例 2:

入力:ポイント = [[1,2],[3,4],[5,6],[7,8]]
出力: 4
説明:各風船は矢を放つ必要があり、合計 4 本の矢が必要です。

例 3:

入力:ポイント = [[1,2],[2,3],[3,4],[4,5]]
出力: 2
説明: 風船は 2 本の矢印で割ることができます。
- x = 2 に矢を放ち、風船 [1,2] と [2,3] を割ります。
- x = 4 に矢を放ち、風船 [3,4] と [4,5] を割ります。

ヒント:

  • 1 <= points.length <= 105
  • points[i].length == 2
  • -231 <= xstart < xend <= 231 - 1

思考プロセスと知識ポイント: 

バルーンができるだけ重なるようにするには、配列をソートする必要があります

次に、バルーンの開始位置で並べ替えますか、それともバルーンの終了位置で並べ替えますか?

実際、誰でもできるのです!ただ、対応する横断順序が異なるので、吹き出しの開始位置に従って並べ替えました。

開始位置に従ってソートされているので、風船の配列を前から後ろに移動し、できるだけ左側に風船を繰り返すようにします。

前から後ろにトラバースしていて、重なっている気球に遭遇した場合はどうすればよいですか?

バルーンが重なっている場合、重なっているバルーンの右側の境界の最小値の前の間隔には矢印が必要です


 答え:

class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if (points.size() == 0) return 0;
        sort(points.begin(), points.end(), cmp);

        int result = 1; // points 不为空至少需要一支箭
        for (int i = 1; i < points.size(); i++) {
            if (points[i][0] > points[i - 1][1]) {  // 气球i和气球i-1不挨着,注意这里不是>=
                result++; // 需要一支箭
            }
            else {  // 气球i和气球i-1挨着
                points[i][1] = min(points[i - 1][1], points[i][1]); // 更新重叠气球最小右边界
            }
        }
        return result;
    }
};

  トピック:

435. 重複しない間隔

間隔のコレクションが与えられるとします intervals 。ここで、 は です intervals[i] = [starti, endi] 。残りの間隔が重ならないように削除する必要がある間隔の最小数を 返します 

例 1:

入力:間隔 = [[1,2],[2,3],[3,4],[1,3]]
出力: 1
説明: [1,3] を削除した後、残りの間隔は重複しません。

例 2:

入力:間隔 = [ [1,2], [1,2], [1,2] ]
出力: 2
説明:残りの間隔が重複しないようにするには、両方の [1,2] を削除する必要があります。

例 3:

入力:間隔 = [ [1,2], [2,3] ]
出力: 0
説明:間隔は既に重複していないため、削除する必要はありません。

ヒント:

  • 1 <= intervals.length <= 105
  • intervals[i].length == 2
  • -5 * 104 <= starti < endi <= 5 * 104

思考プロセスと知識ポイント: 

右の境界で並べ替えて、交差しない間隔の数を左から右に記録してみましょう。最後に、区間の合計数から非交差区間の数を減算して、削除する必要がある区間の数を取得します

このとき問題となるのは、交差しない区間の最大数を求めることである。

間隔 1 と間隔 2 が重なっていると判断された後、間隔 3 で再投稿するかどうかをどのように判断すればよいでしょうか?

区間 1 と区間 2 の右側の境界の最小値を取ることです。この最小値より前の部分は区間 1 と区間 2 の重なっている部分でなければなりません。この最小値が区間 3 にも達する場合は、次のことを意味します。間隔 1、2、3 はすべて一致しています。

次のステップは、間隔 4 から開始して、間隔 1 の終わりよりも大きい間隔を見つけることです。

間隔 4 が終了すると間隔 6 が見つかるため、記録された非交差間隔の総数は 3 になります。

区間の合計数は 6 から、交差しない区間の数 3 を引いたものになります。削除する間隔の最小数は 3 です。


 答え:

class Solution {
public:
    // 按照区间右边界排序
    static bool cmp (const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 1; // 记录非交叉区间的个数
        int end = intervals[0][1]; // 记录区间分割点
        for (int i = 1; i < intervals.size(); i++) {
            if (end <= intervals[i][0]) {
                end = intervals[i][1];
                count++;
            }
        }
        return intervals.size() - count;
    }
};

  トピック:

763. アルファベットの範囲を分割する

文字列を与えます s 。この文字列をできるだけ多くのフラグメントに分割する必要があり、同じ文字が現れるのは最大でも 1 つのフラグメントです。

すべての除算結果を順番に接続すると、結果の文字列は依然として になります s 。

各文字列フラグメントの長さを表すリストを返します。

例 1:

入力: s = "ababcbacadefegdehijhklij"
出力: [9,7,8]
説明:
除算結果は「ababcbaca」、「defegde」、「hijhklij」です。
各文字は最大でも 1 つのフラグメントに表示されます。
「ababcbacadefegde」、「hijhklij」のような除算は、除算のフラグメントの数が少ないため、間違っています。

例 2:

入力: s = "eccbbbbdec"
出力: [10]

ヒント:

  • 1 <= s.length <= 500
  • s 小文字の英字のみで構成されます

思考プロセスと知識ポイント: 

文字列を分割するというと遡ることを思い浮かべますが、この問題では遡って激しく探す必要はありません。

タイトルでは、同じ文字は最大でも 1 つのセグメントに出現する必要がありますが、同じ文字を同じ間隔で囲むにはどうすればよいでしょうか。

この種の話題に触れたことがない場合、それは非常に困難です。

走査の過程では、各文字の境界を見つけることに相当し、これまでに走査したすべての文字の中で最も遠い境界を見つけた場合、その境界が分割点であることを意味します現時点では、すべての文字が以前に出現しており、最も遠い文字がこの境界に達します。

次の 2 つのステップに分けることができます。

  • 各文字の最後に出現する個数を数える
  • 文字を先頭から走査し、文字の最も遠い添字を更新します。最も遠い文字の添字が現在の添字と等しいことが判明した場合、分割点が見つかります。

 答え:

class Solution {
public:
    vector<int> partitionLabels(string S) {
        int hash[27] = {0}; // i为字符,hash[i]为字符出现的最后位置
        for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置
            hash[S[i] - 'a'] = i;
        }
        vector<int> result;
        int left = 0;
        int right = 0;
        for (int i = 0; i < S.size(); i++) {
            right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
            if (i == right) {
                result.push_back(right - left + 1);
                left = i + 1;
            }
        }
        return result;
    }
};

「いいね!」、ブックマーク、コメントを歓迎します。あなたの励ましが私の創作の最大の動機です。(๑╹◡╹)ノ"""

著作権に関する声明: この記事は、CSDN ブロガー「Dumengjiu」によるオリジナルの記事であり、CC 4.0 BY-SA 著作権契約に準拠しています。転載する場合は、元のソースのリンクとこの声明を添付してください。
元のリンク: Dumengjiu の blog_CSDN blog-csdn ドメイン ブロガー

おすすめ

転載: blog.csdn.net/weixin_53310927/article/details/131388481