データ構造とアルゴリズム: 単調に増加する数値

データ構造とアルゴリズム: 単調に増加する数値

[[439817]]

単調に増加する数字

LeetCode の問題へのリンク: https://leetcode-cn.com/problems/monotone-increasing-digits

負でない整数 N が与えられた場合、N 以下の最大の整数を見つけます。この整数は、各桁の数字が単調に増加するという事実を満たす必要があります。

(すべての隣接する数字 x と y が x <= y を満たす場合にのみ、整数は単調に増加すると言います。)

例1:

  • 入力: N = 10
  • 出力: 9

例2:

  • 入力: N = 1234
  • 出力: 1234

例3:

  • 入力: N = 332
  • 出力: 299

注: N は [0, 10^9] の範囲の整数です。

力ずくの解決法

質問は非常に単純なので、最初に思いつくのは力ずくの解決法です。私が代わりにやってみますが、結果は当然タイムアウトです!

コードは次のとおりです。

  1. クラスソリューション{
  2. プライベート:
  3. ブールチェック番号( int数値){
  4. 整数 最大= 10;
  5. while (数値) {
  6. int t = 数値 % 10;
  7. 最大値>=t)の場合、最大値=t;
  8. それ以外 戻る 間違い;
  9. 数値 = 数値 / 10;
  10. }
  11. 戻る 真実;
  12. }
  13. 公共
  14. int monotoneIncreasingDigits( int N) {
  15. ( int i = N; i > 0; i --)の場合{  
  16. checkNum(i) の場合、 iを返します
  17. }
  18. 0を返します
  19. }
  20. };
  • 時間計算量: O(n * m) mはnの長さ
  • 空間計算量: O(1)

貪欲アルゴリズム

この問題では、N 以下の最大の単調増加する整数が求められるため、2 桁の数字を例に挙げます。

たとえば、98 の場合です。strNum[i - 1] > strNum[i] (非単調増加) になると、まず strNum[i - 1] -- にして、次に strNum[i] を 9 にして、整数が 89 になるようにします。これは、98 未満の最大の単調増加整数です。

この点をしっかり考えれば、この問題は簡単に解決できるでしょう。

局所最適: strNum[i - 1] > strNum[i] の場合、strNum[i - 1] -- とし、strNum[i] を 9 に設定します。これにより、これらの 2 つの数字が最大の単調増加する整数になることが保証されます。

グローバル最適値: N 以下の単調に増加する最大の整数を取得します。

ただし、局所最適値から大域最適値を導き出すには、トラバース順序とマークの開始点(一律 9 に変更)など、他の条件も必要です。

前から後ろへ移動しますか、それとも後ろから前へ移動しますか?

前から後ろへトラバースする場合、strNum[i - 1] > strNum[i] であれば、strNum[i - 1] を 1 減らします。ただし、このとき strNum[i - 1] を 1 減らすと、strNum[i - 2] より小さくなる可能性があります。

少し抽象的ですが、例えば332という数字を前から後ろへたどっていくと329になります。このとき、2は最初の数字の3より小さいので、実際の結果は299になるはずです。

したがって、前から後ろへトラバースすると、すでにトラバースされた結果が変わります。

次に、後ろから前へ走査することで、最後の比較の結果を再利用できます。332の値は後ろから前へ変化します: 332 -> 329 -> 299

トラバーサル順序が決定された後、局所最適解から大域最適解を推測できます。反例が見つからない場合は、貪欲法を試してください。

C++ コードは次のとおりです。

  1. クラスソリューション{
  2. 公共
  3. int monotoneIncreasingDigits( int N) {
  4. 文字列 strNum = to_string(N);
  5. // フラグは割り当て9の開始位置を示すために使用されます
  6. //フラグに値が割り当てられていない場合に2 番目のforループが実行されないようにするには、このデフォルト値を設定します。
  7. intフラグ = strNum.size ( );
  8. ( int i = strNum.size () - 1; i > 0; i -- ) {  
  9. strNum[i - 1] > strNum[i] の場合 {
  10. フラグ = i;
  11. strNum[i - 1] --;  
  12. }
  13. }
  14. ( int i = flag; i < strNum.size ( ) ); i++) {
  15. strNum[i] = '9' ;
  16. }
  17. stoi(strNum)を返します
  18. }
  19. };
  • 時間計算量: O(n) nは数の長さ
  • 空間計算量: O(n) は文字列を必要とするため、文字列に変換する方が便利である

要約する

この問題では、98 などの 1 つの例についてのみ明確に考える必要があります。strNum[i - 1] > strNum[i] (非単調増加) になると、まず strNum[i - 1] を 1 減らし、strNum[i] に 9 を割り当てて、整数が 89 になるようにします。対応する貪欲な解決策を自然に考えることができます。

貪欲さについて考えるときは、トラバースの順序も考慮する必要があります。後ろから前へトラバースすることによってのみ、最後の比較の結果を再利用できます。

最終的なコードを実装するときには、値 9 の割り当てを開始する場所をマークするためのフラグを使用するなどのいくつかのテクニックも必要です。

その他の言語

ジャワ:

  1. バージョン 1
  2. クラスソリューション{
  3. 公共  int monotoneIncreasingDigits( int N) {
  4. String[] 文字列 = (N + "" ).split( "" );
  5. int開始 = 文字列.長さ;
  6. ( int i = strings.length - 1; i > 0; i --) {  
  7. 整数.parseInt(文字列[i]) <整数.parseInt(文字列[i - 1]) の場合
  8. 文字列[i - 1] = ( Integer .parseInt(文字列[i - 1]) - 1) + "" ;
  9. 開始 = i;
  10. }
  11. }
  12. ( int i = start; i < strings.length; i++) {
  13. 文字列[i] = "9" ;
  14. }
  15. 戻る 整数.parseInt(String . join ( "" , strings));
  16. }
  17. }

Java バージョン 1 では、String 配列が作成され、Integer.parseInt メソッドが複数回使用されるため、時間とスペースの使用量が膨大になり、12 ミリ秒かかります。次のバージョンでは、char 配列をその場で変更するため、1 ミリ秒かかります。

  1. バージョン2
  2. クラスソリューション{
  3. 公共  int monotoneIncreasingDigits( int n) {
  4. (n==0)の場合は0を返します
  5. char [] chars = Integer .toString(n).toCharArray();
  6. int start = Integer .MAX_VALUE; //startの初期値は最大値に設定されます。これは、数値自体が単調増加しているときに、どの桁も9に変更する必要がない状況を防ぐためです。
  7. ( int i=chars.length-1;i>0;i --) {  
  8. (文字[i]<文字[i-1])の場合{
  9. 文字[i-1] --;  
  10. 開始=i;
  11. }
  12. }
  13. StringBuilder res=新しいStringBuilder();
  14. ( int i=0;i<chars.length;i++) {
  15. if (chars[i]== '0' &&i==0) continue ;//09のような状況を防ぐ
  16. (i>=開始)の場合{
  17. res.append( '9' );
  18. }そうでない場合はres.append(chars[i]);
  19. }
  20. 戻る 整数.parseInt(res.toString());
  21. }
  22. }

パイソン:

  1. クラスソリューション:
  2. def monotoneIncreasingDigits(self, n: int ) -> int :
  3. a = リスト(str(n))
  4. iが範囲(len(a)-1,0,-1)内にある場合:
  5. int (a[i]) < int (a[i-1])の場合:
  6. a[i-1] = str( int (a[i-1]) - 1) です。
  7. a[i:] = '9' * (len(a) - i) #pythonはフラグ値を設定する必要はなく、長さに応じて9を与えるだけです
  8. 戻る  int ( "" . join (a))

行く

  1. func monotoneIncreasingDigits(N int ) int {
  2. s := strconv.Itoa(N) //添え字を使いやすくするために数値を文字列に変換する
  3. ss := []byte(s) //簡単に変更できるように文字列をバイト配列に変換します。
  4. n := 長さ(ss)
  5. n <= 1の場合{
  6. Nを返す
  7. }
  8. i:=n-1; i>0; i -- {  
  9. if ss[i-1] > ss[i]{//前の桁が次の桁より大きいので、前の桁を1減らし、次の桁をすべて9に設定します。
  10. ss[i-1] -= 1
  11. j := i ; j < n; j++の場合、後続の数字はすべて 9 に設定されます。
  12. ss[j] = '9'  
  13. }
  14. }
  15. }
  16. res, _ := strconv.Atoi(文字列(ss))
  17. 戻り
  18. }

ジャバスクリプト

  1. var monotoneIncreasingDigits =関数(n) {
  2. n = n.toString()
  3. n = n.split( '' ).map(item => {
  4. 戻る+アイテム
  5. })
  6. フラグ = 無限大
  7. (i = n.length - 1; i > 0; i --)の場合{  
  8. n[i - 1] > n[i]の場合{
  9. フラグ = i
  10. n[i - 1] = n[i - 1] - 1
  11. 9 は
  12. }
  13. }
  14.  
  15. (i = flag; i < n.length; i++)の場合{
  16. 9 は
  17. }
  18.  
  19. n = n.join ( '' )
  20. 戻る+n
  21. };

<<:  1.2兆パラメータ:Googleの汎用スパース言語モデルGLaM、小サンプル学習がGPT-3を上回る

>>:  自動運転のジレンマと選択

ブログ    
ブログ    
ブログ    
ブログ    
ブログ    

推薦する

...

グーグル、規制当局の措置を受けてEUでのチャットボット「バード」のリリースを一時停止

グーグルは6月14日、欧州連合(EU)の主要データ規制当局がプライバシーに関する懸念を表明したため、...

2019年のAI開発の7つの分野

[[257419]] 2018 年は人工知能 (AI) の主流採用をさらに促進し、より多くの機能の提...

調査によると、AIツールは企業の従業員が年間約400時間を節約するのに役立つことがわかった

7月10日、人材分析・計画会社Visierは、英国、米国、カナダ、ドイツの250社以上の企業の従業員...

...

機械学習と従来のプログラミングの違いについて話す

[[264779]] AI と ML は誇張されすぎていて、if 文を書いたりプログラミングに関係す...

張三が試験でカンニングをしたい場合、どのような暗号化アルゴリズムを使用すればよいでしょうか?先生にバレないように?

「平常時に努力しなければ、試験では友達に頼らざるを得なくなる」ということわざがある。試験が近づくに...

2019年のテクノロジートレンド予測: 5Gが爆発的に普及し、人工知能も勢いを増す

テクノロジー業界にとって、2018年は忘れられない年になる運命にある。結局、シェアサイクルのバブルは...

...

...

人工知能の時代に人権と民主主義をどう守るか

人工知能 (AI) システムは近年急速に普及しており、特に 2023 年には大規模言語モデル (LL...

最も人気のある 12 の AI ツール、ライブラリ、プラットフォーム

[[205783]]近年 AI の利用が増えているため、利用可能な AI ツール、ライブラリ、プラッ...

直接的な選好最適化戦略を用いたミストラル7bモデルの微調整

翻訳者|朱 仙中レビュー | Chonglou導入通常、事前トレーニング済みの大規模言語モデル (L...