🙄
LeetCoding Challenge Oct. 11: Remove Duplicate Letters
LeetCode October Challenge の 11 日目。
今日の問題は Remove Duplicate Letters。
問題の概要
- 与えられた文字列から、その文字列で使われている文字をすべて、かつそれぞれ一文字だけ含むサブシーケンスを考える
- このサブシーケンスのうち辞書順的に一番小さい文字列を求めよ
考え方
- うーむ、適切なアルゴリズムがすぐには思いつかない…
- 文字列の末尾から先頭の逆順に文字列を走査して、move-to-front リストでその時点のサブシーケンスを管理してみてはどうか 🤔
- 実際に実装してみて何度か submit してみたが、wrong answer 喰らって直すたびに異なるエラーケースに遭遇してしまう 😭
- このやり方は筋が悪そうなので、別の方法を模索しよう…
- では、文字列の長さ * 27 の大きさの二次元配列
table
を用意し、table[文字列上の位置]
の配列にてその位置以降に文字 a~z が出現する位置を表してみてはどうか?- 先と同じく、文字列末尾から先頭に向かって走査して
table
を完成させる前提だよ -
table[文字列上の位置][26]
には、その位置以降に出現する文字をビットで記した値を入れておくよ- 下位 1 ビット目が a, 2 ビット目が b, という要領だね
- 空間計算量・時間計算量は文字列長を
n
、アルファベットサイズをk
(k = 26) とするとO(n * k)
となり、あまり効率的ではないね 🙈
- 先と同じく、文字列末尾から先頭に向かって走査して
-
table
が完成したら、辞書順に最小のサブシーケンスから順にそれが存在するか否かをtable
を頼りに文字列をたどって確認してみよう- 再帰で書けばよさそう
- 最悪の時間計算量は
O(k^k)
になるのかな… 😱
- ひとまず、この
table
を利用する方法で実装して submit → accept 🤗- Runtime は 3ms で、これは
Your runtime beats 81.40 % of java submissions.
らしい。微妙だなあ… 🙄
- Runtime は 3ms で、これは
コード
class Solution {
public String removeDuplicateLetters(String s) {
if (s.isEmpty()) {
return s;
}
int[][] table = new int[s.length()][27];
int[] current = new int[27];
for (int i = s.length() - 1; i >= 0; i--) {
int letter = s.charAt(i) - 'a';
current[letter] = i;
current[26] |= 1 << letter;
System.arraycopy(current, 0, table[i], 0, 27);
}
int alphabet = current[26];
for (int letter = Integer.numberOfTrailingZeros(alphabet);
letter < 26;
letter += Integer.numberOfTrailingZeros(alphabet >>> (letter + 1)) + 1) {
StringBuilder result = recursive(s, alphabet, table, current[letter], 0);
if (result != null) {
return result.reverse().toString();
}
}
return "";
}
StringBuilder recursive(String s, int alphabet, int[][] table, int index, int usedChars) {
char ch = s.charAt(index);
usedChars |= 1 << (ch - 'a');
if (usedChars == alphabet) {
return new StringBuilder().append(ch);
}
if ((table[index][26] | usedChars) != alphabet) {
// このルートを探索しても解が求まる望みがない
return null;
}
int b = alphabet ^ usedChars;
for (int letter = Integer.numberOfTrailingZeros(b);
letter < 26;
letter += Integer.numberOfTrailingZeros(b >>> (letter + 1)) + 1) {
StringBuilder result = recursive(s, alphabet, table, table[index][letter], usedChars);
if (result != null) {
return result.append(ch);
}
}
return null;
}
}
Discussion