LeetCode 2108. Find First Palindromic String in the Array
はじめに
LeetCode 「2108. Find First Palindromic String in the Array」の問題をGoで解きました。
問題
問題文
Given an array of strings words
, return the first palindromic string in the array. If there is no such string, return an empty string ""
.
A string is palindromic if it reads the same forward and backward.
和訳
文字列の配列 words
が与えられた場合、その配列の最初の回文の文字列を返す。そのような文字列がない場合は、空の文字列 ""
を返す。
文字列が回文であるのは、前後に同じ読み方をする場合である。
例
Input: words = ["abc","car","ada","racecar","cool"]
Output: "ada"
Explanation: The first string that is palindromic is "ada".
Note that "racecar" is also palindromic, but it is not the first.
Input: words = ["notapalindrome","racecar"]
Output: "racecar"
Explanation: The first and only string that is palindromic is "racecar".
Input: words = ["def","ghi"]
Output: ""
Explanation: There are no palindromic strings, so the empty string is returned.
制約
- 1 <= words.length <= 100
- 1 <= words[i].length <= 100
- words[i] consists only of lowercase English letters.
解答1(Two Pointers)
今回の問題では、文字列が回文かどうかを判定する処理が必要となります。
回文かどうかの判定は、文字列の先頭・末尾から中央に向かって1文字ずつ文字を比較していくことで実現できます。
Two Pointers パターンが使えそうです。
Two Pointers パターン
Two Pointers は、2つの異なる位置からポインタを動かしながら探索を行う方法です。
主に配列や文字列操作を効率化するために使われ、時間計算量をO(n)に抑えることができるのがメリットです。
回文判定では 1回のループで 2文字をチェックできるため、対称性を活かして無駄な計算を減らせます。
コード
func firstPalindrome(words []string) string {
for _, word := range words {
if isPalindromic(word) {
return word
}
}
return ""
}
// 文字列が回文かどうかを判定する
func isPalindromic(s string) bool {
left, right := 0, len(s)-1
for left < right {
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
isPalindromic
で、left
は左から探索を行うポインタ、right
は右から探索を行うポインタです。
ポインタを動かしながら両端から1文字ずつ比較していき、ループを抜けた場合にtrueを返しています。
計算量
- 時間的計算量: O(n * m)
-
m
はword
の長さ、n
はwords
の長さ
-
- 空間的計算量: O(1)
解答2(reverse を使う方法)
最適解ではないものの、別のアプローチもあるので紹介します。
文字列を反転させる処理を作り、文字列が回文かどうかを判定する方法です。
コード
func firstPalindrome(words []string) string {
for _, word := range words {
if word == reverse(word) {
return word
}
}
return ""
}
// 文字列を反転させる(ASCII のみなので `[]byte` を使用)
// Unicode 文字(漢字・絵文字など)が含まれる場合は `[]rune` を使う
func reverse(s string) string {
arr := []byte(s)
left, right := 0, len(arr)-1
for left < right {
arr[left], arr[right] = arr[right], arr[left]
left++
right--
}
return string(arr)
}
計算量
- 時間的計算量: O(n * m)
-
m
はword
の長さ、n
はwords
の長さ
-
- 空間的計算量: O(m)
reverseを使う方法では、新しいスライスを作成して文字列を逆順に格納するため、追加のメモリが必要となります。
そのため、メモリ効率を考えるなら Two Pointers の方法の方が良い選択肢になります。
参考
Two Pointers パターンを使用する別の問題を以下の記事で紹介しています。
Discussion