C++で同型文字列(アイソモーフィック)を判定する方法
同型文字列とは?
2つの文字列 s と t が与えられたとき、両者が「同型(isomorphic)」であるかどうかを判定する問題を考えてみましょう。同型文字列とは、s の文字を適切に置き換えることで t が得られるような文字列のペアのことです。
ただし、置き換えには以下のルールがあります。
- ある文字が出現する箇所は、すべて同じ文字に置き換えなければなりません
- 文字の出現順序は保持される必要があります
- 2つの異なる文字が同じ文字へマップされることは許されません
- ただし、1つの文字が自分自身にマップされることは可能です
例えば、s = "egg"、t = "add" という入力の場合、e → a、g → d という一対一の対応関係が成り立つため、出力は true になります。
解法のアルゴリズム
この問題は、各文字の対応関係(マッピング)を記録しながら文字列を走査することで効率的に解けます。手順は以下の通りです。
- サイズ256の配列 arr を定義し、すべて -1 で初期化します(文字間のマッピングを記録)
- サイズ256の配列 visited を定義し、すべて 0 で初期化します(s 側の文字の使用状況を管理)
- サイズ256の配列 visited1 を定義し、すべて 0 で初期化します(t 側の文字の使用状況を管理)
- i := 0 から文字列 s の長さ未満の間、i を1ずつ増やしながら以下の処理を繰り返します。
- visited[s[i]] が 1、または visited1[t[i]] が 1 の場合:
- arr[s[i]] が t[i] - 'a'(ASCIIコードの差分)と等しくなければ false を返します
- そうでない場合:
- visited[s[i]] := 1 と設定します
- visited1[t[i]] := 1 と設定します
- arr[s[i]] := t[i] - 'a' としてマッピングを登録します
- visited[s[i]] が 1、または visited1[t[i]] が 1 の場合:
- ループが最後まで完了したら true を返します
C++実装例
それでは、実際のC++コードを見ていきましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
bool isIsomorphic(string s, string t) {
vector<int> arr(256, -1);
vector<bool> visited(256, 0);
vector<bool> visited1(256, 0);
for (int i = 0; i < s.length(); i++) {
if (visited[s[i]] == 1 || visited1[t[i]] == 1) {
if (arr[s[i]] != t[i] - 'a') {
return false;
}
}
else {
visited[s[i]] = 1;
visited1[t[i]] = 1;
arr[s[i]] = t[i] - 'a';
}
}
return true;
}
};
main(){
Solution ob;
cout << (ob.isIsomorphic("sky","fry"));
}入力
"sky","fry"
出力
1
コードの解説
この例では、"sky" と "fry" を比較しています。s → f、k → r、y → y という一対一の対応が正しく成り立つため、結果として 1(true)が出力されます。
このアルゴリズムのポイントは、visited と visited1 という2つの使用済みフラグ配列を用意している点です。これにより、「s の異なる複数の文字が t の同じ1文字にマップされる」という不正なケースも確実に検出できます。例えば s = "ab"、t = "aa" の場合、最初の a → a というマッピングが登録された後、b → a を登録しようとした時点で visited1['a'] が既に 1 になっているため、正しく false と判定されます。
計算量は、文字列の長さを n とすると時間計算量 O(n)、固定サイズの配列のみを使用するため空間計算量 O(1) となり、非常に効率的な実装と言えます。
-
C++で2つの文字列をマージして最大の文字列を作成する方法
2つの文字列「a」と「b」、および結果を格納するための文字列「merge」が与えられているとします。この課題は、次のルールに従って「a」と「b」から1文字ずつ取り出し、「merge」を埋めていくことです。 文字列「a」が空でない場合、「a」の先頭の1文字を取り除き、その文字を「merge」に追加します。 文字列「b」が空でない場合、「b」の先頭の1文字を取り除き、その文字を「merge」に追加します。 両方の文字列が空でない場合は、それぞれの残りの文字列を辞書順に比較し、より大きい方の先頭文字を「merge」へ移します。たとえば「a」が「b」より大きい場合は、先に「a」から文字を取り出します
-
C++で二分木が同型(アイソモーフィック)かどうかを判定する方法
二分木では、各ノードが「左の子」と「右の子」という2つの子ノードを持ちます。ここでは、2つの二分木が与えられたとき、一方の木を左右反転(フリップ)することでもう一方の木が得られるかどうかを判定する問題を解説します。一方の木を反転することでもう一方の木と同じ構造が得られる場合、その2つの木は「同型(アイソモーフィック)」であると定義されます。具体例入力1出力Isomorphic(同型)説明:Tree-2はTree-1を左右反転することで得られるため、この2つの木は同型です。解き方のアプローチこの問題は再帰的なアプローチで効率的に解くことができます。ブール型の関数を用意し、両方の木のルートノードを