Pythonで最大2種類の異なる文字を含む最長部分文字列の長さを求める方法
問題概要
文字列 s が与えられたとき、「異なる文字が最大2種類しか含まれない最長の部分文字列」の長さを求めることを考えます。
例えば、入力が s = "xyzzy" の場合、出力は 4 になります。これは「yzzy」が y と z の2種類の文字のみを含む最長の部分文字列だからです。
解き方:スライディングウィンドウ
この問題は「スライディングウィンドウ(尺取り法)」と呼ばれる手法を使うことで効率的に解けます。各文字の出現回数を記録するマップ(Counter)を用意し、ウィンドウ内の異なる文字の種類数が2を超えないように、左端を調整しながら右端を伸ばしていきます。
具体的な手順は以下の通りです。
start := 0(ウィンドウの左端の位置)c := 各文字の出現回数を保持するマップans := 0(答えとなる最長の長さ)endを 0 から文字列の長さまで順に動かしながら、以下を繰り返します。c[s[end]]を1増やす(右端の文字をウィンドウに追加)cのサイズ(異なる文字の種類数)が2を超えている間、次の処理を繰り返します。c[s[start]]を1減らすc[s[start]]が0になった場合は、そのキーをマップから削除するstartを1進める
ansをansと(end - start + 1)の大きい方の値で更新する
最後に
ansを返します。
実装例(Python)
それでは、実際のコードを見てみましょう。
class Solution: def solve(self, s): from collections import Counter start = 0 c = Counter() ans = 0 for end in range(len(s)): c[s[end]] += 1 while len(c) > 2: c[s[start]] -= 1 if not c[s[start]]: del c[s[start]] start += 1 ans = max(ans, end - start + 1) return ans ob = Solution() s = "xyzzy" print(ob.solve(s))
入力
s = "xyzzy"
出力
4
計算量について
このアルゴリズムでは、end と start がそれぞれ文字列の長さ分しか移動しないため、時間計算量は O(n) です。また、マップが保持するキーの数は異なる文字の種類数に依存するため、空間計算量も十分に小さく抑えられます。文字列が長くなっても高速に動作するのが、この手法の大きな利点です。
-
Pythonで二分木の最小共通祖先(LCA)を求めるアルゴリズムと実装例
はじめに二分木と2つの数値 a、b が与えられたとき、a と b を子孫として持つ最も深いノード(最小共通祖先:LCA)の値を求める問題を考えてみましょう。ここで重要なポイントは、「あるノードはそれ自身の子孫にもなり得る」という点です。つまり、片方のノードがもう片方の祖先である場合、そのノード自体が答えになります。例以下のような二分木を考えます。このとき、a = 6、b = 2 とすると、出力は 4 になります。値4のノードが、6と2の両方を子孫として持つ最も深いノードだからです。解法のアプローチこの問題は再帰を使って効率的に解くことができます。手順は以下の通りです。solve() メソッドを
-
Pythonで数値が2の累乗かどうかを判定するプログラム
本記事では、与えられた数値が2の累乗(べき乗)であるかどうかを判定する方法について、考え方と実装手順をわかりやすく解説します。 問題の定義 ある整数 n が与えられたとき、その数が2の累乗(1, 2, 4, 8, 16, …)であるかどうかを判定します。 アプローチ 判定には「繰り返し2で割る」というシンプルな方法を使います。考え方は以下の通りです。 入力された数値 n を、1になるまで繰り返し2で割っていきます(n = n // 2)。 割る過程で n % 2 の結果が0以外(奇数)になり、かつ n が1でない場合は、その数は2の累乗ではありません。 最終的に n がちょうど1になれば、そ