[C++ コード] シミュレーション、動的プログラミング、繰り返し文字を含まない最長の部分文字列、2 つの正の次数の配列の中央値を求める、最長の回文部分文字列

题目:两数相加

  • 負でない 2 つの整数を表す、2 つの空でないリンク リストを提供します。各桁は逆順で保存され、各ノードは 1 桁のみ< a i=6> 数値。 2 つの数値を加算し、その合計を同じ形式で表すリンク リストを返してください。数字の 0 を除いて、どちらの数字も 0 で始まることはないと考えることができます。

  • ここに画像の説明を挿入します

  • 2 つの入力リンク リストは両方とも逆順で桁数を格納するため、2 つのリンク リストの同じ位置にある数値は直接加算することも可能です。 2 つのリンクされたリストを同時に走査し、それらの合計をビットごとに計算し、それを現在位置の桁上げ値に加算します。具体的には、現在の 2 つの連結リストの対応する位置の数値が n1 と n2 で、キャリーの値がキャリーである場合、それらの和は n1+n2+キャリーとなり、そのうち、回答の対応する位置の数値が連結されます。リストは (n1+n2+キャリー) mod=10 です。 2 つのリンク リストの長さが異なる場合は、短い方のリンク リストの末尾にいくつかの 0 があると考えることができます。 2 つのリンク リストの長さが異なる場合は、短い方のリンク リストの末尾にいくつかの 0 があると考えることができます。

  • /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode() : val(0), next(nullptr) {}
     *     ListNode(int x) : val(x), next(nullptr) {}
     *     ListNode(int x, ListNode *next) : val(x), next(next) {}
     * };
     */
    class Solution {
          
          
    public:
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
          
          
            ListNode *head=nullptr,*tail=nullptr;
            int carry=0;
            while(l1 || l2){
          
          
                int n1 = l1?l1->val:0;
                int n2 = l2?l2->val:0;
                int sum = n1+n2+carry;
                if(!head){
          
          
                    head = tail = new ListNode(sum%10);
                }else{
          
          
                    tail->next=new ListNode(sum%10);
                    tail=tail->next;
                }
                carry=sum/10;
                if(l1){
          
          
                    l1=l1->next;
                }
                if(l2){
          
          
                    l2=l2->next;
                }
            }
            if(carry>0){
          
          
                tail->next=new ListNode(carry);
            }
            return head;
        }
    };
    
  • 時間計算量: O(max⁡(m,n))、ここで、m と n はそれぞれ 2 つのリンクされたリストの長さです。 2 つのリンクされたリストのすべての位置をトラバースする必要があり、各位置の処理には O(1) 時間しかかかりません。空間複雑度: O(1)。戻り値は空間の複雑さにはカウントされないことに注意してください。

タイトル:繰り返し文字を含まない最長の部分文字列

  • 指定された文字列sから、繰り返し文字を含まない最長の部分文字列を見つけてください

  • 実際には、質問例の abcabcbb のようなキューですが、このキュー(ウィンドウ)に入ると質問の要件を満たす abc になりますが、再度 a を入力するとキューは abc となり、現時点では要件を満たしていません。そこで、このキューを移動します。質問の要件が満たされるまで、キューの左側にある要素を削除するだけです。このようなキューを維持し、キューの最長の長さを見つけて、解決策を見つけてください。

  • class Solution {
          
          
    public:
        int lengthOfLongestSubstring(string s) {
          
          
            if(s.size()==0){
          
          
                return 0;
            }
            unordered_set<char> temp;
            int max_one=0;
            int left=0;
            for(int i=0;i<s.size();i++){
          
          
                while(temp.find(s[i])!=temp.end()){
          
          
                    temp.erase(s[left]);
                    left++;
                }
                max_one = max(max_one,i-left+1);
                temp.insert(s[i]);
            }
            return max_one;
        }
    };
    
  • unowned_set コンテナは、文字通り「順序のないセット コンテナ」と翻訳できます。つまり、unowned_set コンテナは set コンテナと非常によく似ています。唯一の違いは、set コンテナは格納されたデータをそれ自体で並べ替えるのに対し、unowned_set コンテナは格納されたデータを独自に並べ替えることです。コンテナにはありません。順序なしセット (unowned_set) は、ハッシュ テーブルを使用して実装された順序なしの連想コンテナです。キーはハッシュ テーブルのインデックス位置にハッシュされるため、挿入操作は常にランダムです。順序なしセットに対するすべての演算は、平均的な場合、一定の時間計算量 O(1) になりますが、最悪の場合、時間計算量は線形時間 O(n) に達する可能性があります。 これはハッシュによって異なります。内部的に使用される関数ですが、実際には非常に優れたパフォーマンスを発揮し、定数時間の検索操作を提供することがよくあります。

タイトル:2 つの正の次数の配列の中央値を求める

  • サイズがそれぞれ mn の 2 つの正の順序 (小さいものから大きいものへ) の配列が与えられます nums1 と < /span>を見つけて返してください。 中央値nums2。これら 2 つの正の次数の配列の

  • 乱暴な解決策: merge メソッドを使用して 2 つの順序付けされた配列をマージし、大きな順序付けされた配列を取得します。大きな順序配列の中央にある要素が中央値です。時間計算量は O(m+n)、空間計算量は O(m+n) です。

  • 2 つのソートされた配列をマージする必要はなく、中央値の位置を見つけるだけです。 2 つの配列の長さは既知であるため、中央値に対応する 2 つの配列の添え字の合計もわかります。最初に 2 つの配列の添え字 000 を指す 2 つのポインタを維持します。そのたびに、小さい方の値を指すポインタが 1 ビット前に移動します (1 つのポインタが配列の末尾に到達した場合は、ポインタを移動するだけで済みます) ) 中央位置に達するまで。空間計算量は O(1) に減少しますが、時間計算量は依然として O(m+n) です。

  • 時間計算量の要件が対数の場合、通常は二分探索が必要ですが、この質問も二分探索で実装できます。中央値の定義によれば、m+n が奇数の場合、中央値は 2 つの順序配列の (m+n)/2 番目の要素、m+n が偶数の場合、中央値は 2 つの要素の平均になります。順序付けされた配列の (m+n)/2 番目の要素と (m+n)/2+1 番目の要素。したがって、この問題は、2 つの順序付けされた配列で k 番目に小さい数を見つけることに変換できます。ここで、k は (m+n)/2 または (m+n)/2+1 です。

  • class Solution {
          
          
    public:
    
        int help(const vector<int> &nums1,const vector<int> &nums2,int k){
          
          
            int m=nums1.size();
            int n=nums2.size();
            int index1=0,index2=0;
            while(true){
          
          
                if(index1==m){
          
          
                    return nums2[index2+k-1];
                }
                if(index2==n){
          
          
                    return nums1[index1+k-1];
                }
                if(k==1){
          
          
                    return min(nums1[index1],nums2[index2]);
                }
                int newIndex1=min(index1+k/2-1,m-1);
                int newindex2=min(index2+k/2-1,n-1);
                int p1=nums1[newIndex1];
                int p2=nums2[newindex2];
                if(p1<=p2){
          
          
                    k-=newIndex1-index1+1;
                    index1=newIndex1+1;
                }else{
          
          
                    k-=newindex2-index2+1;
                    index2=newindex2+1;
                }
            }
        }
        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
          
          
            int len=nums1.size()+nums2.size();
            if(len&1){
          
          
                return help(nums1,nums2,(len+1)/2);
            }else{
          
          
                return (help(nums1,nums2,len/2)+help(nums1,nums2,len/2+1))/2.0;
            }
        }
    };
    
  • 主な考え方: k 番目 (k>1) の最小要素を見つけるには、pivot1 = nums1[k/2-1] と pivot2 = nums2[k/2-1] を比較します (ここでの「/」は整数の除算を意味します) )、pivot1 以下の nums1 の要素には nums1[0 … k/2-2] が含まれ、合計 k/2-1 になります。pivot2 以下の nums2 の要素には nums2[0 … k/2-2]、合計 k/2 -1; pivot = min(pivot1, pivot2) とします。2 つの配列内の pivot 以下の要素の合計数は (k/2-1) + を超えません。 (k/2-1) <= k-2。このように、pivot 自体は k-1 番目に小さい要素のみになることができ、pivot = pivot1 の場合、nums1[0 ... k/2-1] は k-1 番目に小さい要素にはなりません。これらすべての要素を「削除」すると、残りが新しい nums1 配列として使用されます。pivot = pivot2 の場合、nums2[0 ... k/2-1] を k 番目に小さい要素にすることはできません。これらすべての要素を「削除」し、残りを新しい nums2 配列として使用します。いくつかの要素を「削除」するため (これらの要素は k 番目に小さい要素より小さい)、k の値から値を引いた値を変更する必要があります。削除された番号の数。

トピック:最長の回文部分文字列

  • 文字列 s を指定して、s 内の最長の回文部分文字列を見つけます。文字列の逆順が元の文字列と同じである場合、その文字列は回文文字列と呼ばれます。

  • 部分文字列の場合、それが回文文字列で長さが 222 より大きい場合、最初と最後の 2 文字を削除しても、それは回文文字列のままです。たとえば、文字列「ababa」の場合、「bab」が回文文字列であることがすでにわかっている場合、「ababa」の最初の文字と最後の文字は両方とも「a」であるため、「ababa」は回文文字列である必要があります。

  • この考えに基づいて、動的計画法を使用してこの問題を解決できます。 P(i,j) は、文字列 s の i ~ j 番目の文字からなる文字列 (以下、s[i:j] と表記します) が回文文字列であるかどうかを示します。次に、動的プログラミングの状態遷移方程式を書くことができます。

    • P ( i , j ) = P ( i + 1 , j − 1 ) ∧ ( S i = = S j ) P(i,j)=P(i+1,j−1)∧(S_i ==S_j)P(i,j)=P(i+1j1)(Si ==Sj )

    • s[i:j] が回文であるのは、s[i+1:j−1] が回文であり、s の i 番目と j 番目の文字が同じである場合のみです。

  • class Solution {
          
          
    public:
        string longestPalindrome(string s) {
          
          
            int n=s.size();
            if(n<2){
          
          
                return s;
            }
            int res=1;
            int begin=0;
            vector<vector<bool>> dp(n,vector<bool>(n));
            for(int i=0;i<n;i++){
          
          
                dp[i][i]=true;
            }
            for(int L=2;L<=n;L++){
          
          
                for(int i=0;i<n;i++){
          
          
                    int j=L+i-1;// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                    if(j>=n){
          
          
                        break;
                    }
                    if(s[i]!=s[j]){
          
          
                        dp[i][j]=false;
                    }else{
          
          
                        if(j-i<3){
          
          
                            dp[i][j]=true;
                        }else{
          
          
                            dp[i][j]=dp[i+1][j-1];
                        }
                    }
                    if(dp[i][j] && j-i+1>res){
          
          
                        res=j-i+1;
                        begin=i;
                    }
                }
            }
            return s.substr(begin,res);
        }
    };
    

タイトル:N グリフ変換

  • 指定された文字列を、指定された行数に従って上から下、左から右にジグザグ パターンでs配置しますnumRows。たとえば、入力文字列が "PAYPALISHIRING"、行数が 3 の場合、次のようになります。

    • P   A   H   N
      A P L S I I G
      Y   I   R
      
  • の後は、出力を左から右に 1 行ずつ読み取って、"PAHNAPLSIIGYIR" などの新しい文字列を生成する必要があります。文字列を指定した行数に変換する関数 string Convert(string s, int numRows); を実装してください。

  • n を文字列 s の長さ、r=numRows とします。 r=1 (1 行のみ) または r≥n (1 列のみ) の場合、答えは s と同じであり、s を直接返すことができます。残りのケースでは、2 次元行列を作成し、その行列上でジグザグ パターンで文字列 s を埋め、最後に行列を 1 行ずつスキャンして非 null 文字を探して答えを形成することを検討してください。

  • 質問の意味からすると、マトリックス上に文字を埋める場合、下に r 文字を埋め、次に右に r−2 文字を埋めていき、最後に最初の行に戻ることになるので、 Z 型変換 t=r+r -2=2r-2 では、各期間は行列上の 1+r-2=r-1 列を占めます。

  • したがって、 ⌈ n t ⌉ \Big\lceil\dfrac{n}{t}\Big\rceil となります。tn サイクル (最後のサイクルは完全なサイクルとみなされます) に各サイクルの列数を乗じて行列の列数を取得します c = ⌈ n t ⌉ ⋅ ( r − 1 ) c=\Big\lceil\dfrac{n}{t}\Big\rceil\cdot(r-1) c=tn (r1)。 r 行、c 列の行列を作成し、文字列 s を反復処理してジグザグ パターンに埋めます。具体的には、現在埋められている位置を (x, y)、つまり行列の x 行と y 列とします。初期 (x,y)=(0,0)、これは行列の左上隅です。現在の文字添字 i が i または mod o d > t < r − 1 i\bmod tに対してt<;r1 の場合は下に移動し、それ以外の場合は右上に移動します。

  • class Solution {
    public:
        string convert(string s, int numRows) {
            int n=s.size(),r=numRows;
            if(r==1||r>=n){
                return s;
            }
            int t=r*2-2;
            int c=(n+t-1)/(t)*(t-1);
            vector<string> temp(r,string(c,0));
            for(int i=0,x=0,y=0;i<n;i++){
                temp[x][y]=s[i];
                if(i%t<r-1){
                    x++;
                }else{
                    x--;
                    y++;
                }
            }
            string res;
            for(auto &row:temp){
                for(char ch:row){
                    if(ch){
                        res+=ch;
                    }
                }
            }
            return res;
        }
    };
    
    
    

おすすめ

転載: blog.csdn.net/weixin_43424450/article/details/134367831