C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で解く「キーボード行」問題:同じ行の文字だけで入力できる単語の見つけ方


英単語のリストが与えられたとき、標準的なQWERTYキーボードレイアウトの同じ行に含まれる文字だけを使って入力できる単語をすべて見つけることを考えます。

例として、入力が ["hello", "world", "mom", "dad", "try", "type", "tom"] であれば、出力は ["dad", "try", "type"] となります。"dad" は中段の文字だけで、「try」と「type」は上段の文字だけで構成されているためです。

キーボードの行と文字の対応

標準的なQWERTY配列は、次の3つの行に分けられます。

  • 1行目(上段):q, w, e, r, t, y, u, i, o, p

  • 2行目(中段):a, s, d, f, g, h, j, k, l

  • 3行目(下段):z, x, c, v, b, n, m

解法の手順

この問題を解くために、以下のステップに従います。

  • 結果を格納するための配列 output を定義します。

  • フラグ oneRow を true で初期化します。

  • マップ charToRowMap を定義し、{文字, 行番号} のペアをすべて登録します。キーはキーボード上に存在する英字、値はその文字が属する行番号です。

  • words 配列内の各単語に対して、以下の処理を繰り返します。

    • 単語が空でない場合は、まず oneRow を true にリセットします。

    • 先頭文字を小文字に変換し、charToRowMap から対応する行番号 row を取得します。

    • i = 1 から始めて、i が単語の長さ未満である間、i を1ずつ増やしながら次を繰り返します。

      • charToRowMap[tolower(word[i])] が row と等しくない場合は、oneRow を false に設定し、ループを抜けます。

    • ループ後も oneRow が true(非ゼロ)であれば、その単語を output の末尾に追加します。

  • 最後に output を返します。

実装例

それでは、理解を深めるために以下の実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
    cout << "[";
    for(int i = 0; i<v.size(); i++){
       cout << v[i] << ", ";
    }
    cout << "]"<<endl;
}
class Solution {
public:
    vector<string> findWords(vector<string>& words) {
       vector<string> output;
       bool oneRow = true;
       unordered_map<char, int> charToRowMap{
          { 'q', 1 }, { 'w', 1 }, { 'e', 1 }, { 'r', 1 }, { 't', 1 }, { 'y', 1 }, { 'u', 1 },
{ 'i', 1 }, { 'o', 1 }, { 'p', 1 }, { 'a', 2 }, { 's', 2 }, { 'd', 2 }, { 'f', 2 }, { 'g', 2 }, { 'h', 2 }, { 'j', 2 }, { 'k', 2 }, { 'l', 2 }, { 'z', 3 }, { 'x', 3 }, { 'c', 3 }, { 'v', 3 }, { 'b', 3 }, { 'n', 3 }, { 'm', 3 }
       };
       for (auto word : words)
       if (!word.empty()) {
          oneRow = true;
          int row = charToRowMap[tolower(word[0])];
          for (int i = 1; i < word.length(); i++)
          if (charToRowMap[tolower(word[i])] != row) {
             oneRow = false;
             break;
          }
          if (oneRow)
          output.push_back(word);
       }
       return output;
    }
};
main(){
    Solution ob;
    vector<string> v = {"hello","world","mom","dad","try","type","tom"};
    print_vector(ob.findWords(v));
}

入力

{"hello","world","mom","dad","try","type","tom"}

出力

[dad, try, type]

計算量の評価

時間計算量は O(N × M) です。ここで N は単語の総数、M は単語の平均の長さを表します。各単語について、最大でもその長さ分だけ文字を走査するためです。一方、空間計算量は O(1)(26文字分のマップを保持するため厳密には O(26))となり、使用する追加データは定数個にとどまります。


  1. C++で数独を解く!バックトラッキングによる数独ソルバーの実装方法

    9×9のマス目に並んだ数字のパズル「数独(Sudoku)」を、プログラムで自動的に解く方法を解説します。数独は9×9の数字グリッドから成り、その全体がさらに3×3のブロック(ボックス)に分割されているのが特徴です。数独を解くための基本ルール使用するのは1から9までの数字のみです。同じ行、同じ列、同じ3×3ブロック内に、同じ数字を重複させて配置することはできません。バックトラッキングによる解法ここでは「バックトラッキング」という手法を用いて数独を解きます。バックトラッキングとは、空いているセルに仮に数字を入れてみて、その配置が正しいかどうかを検証する方法です。もし配置が不正であれば別の数字を試し

  2. C++で二分木の各階層における最大値を見つける方法

    二分木が与えられたとき、その木の各階層(レベル)ごとの最大値を求めることを考えます。例えば、次のような二分木があるとします。この場合、出力は [1, 3, 9] となります。ルート(最上位)の階層には「1」だけが存在するため、最大値は 1第1階層には「3」と「2」があり、最大値は 3第2階層には「5」「3」「9」があり、最大値は 9解決のためのアプローチこの問題は、再帰的な深さ優先探索(DFS) を使うことで簡潔に解くことができます。手順は以下の通りです。結果を格納するための配列 ans を定義します。再帰関数 solve() を定義します。この関数はツリーノードとレベル(初期値は 0)を引数