题目描述
给你一个字符串数组 words
。words
中每个元素都是一个包含 两个 小写英文字母的单词。
请你从 words
中选择一些元素并按 任意顺序 连接它们,并得到一个 尽可能长的回文串 。每个元素 至多 只能使用一次。
请你返回你能得到的最长回文串的 长度 。如果没办法得到任何一个回文串,请你返回 0
。
回文串 指的是从前往后和从后往前读一样的字符串。
示例 1:
输入:words = ["lc","cl","gg"]
输出:6
解释:一个最长的回文串为 "lc" + "gg" + "cl" = "lcggcl" ,长度为 6 。
"clgglc" 是另一个可以得到的最长回文串。
示例 2:
输入:words = ["ab","ty","yt","lc","cl","ab"]
输出:8
解释:最长回文串是 "ty" + "lc" + "cl" + "yt" = "tylcclyt" ,长度为 8 。
"lcyttycl" 是另一个可以得到的最长回文串。
示例 3:
输入:words = ["cc","ll","xx"]
输出:2
解释:最长回文串是 "cc" ,长度为 2 。
"ll" 是另一个可以得到的最长回文串。"xx" 也是。
提示:
1 <= words.length <= 105
words[i].length == 2
words[i]
仅包含小写英文字母。
解法
方法一:贪心 + 哈希表
我们先用哈希表 cnt
统计每个单词出现的次数。
遍历 cnt
中的每个单词 $k$ 以及其出现次数 $v$:
如果 $k$ 中两个字母相同,那么我们可以将 $\left \lfloor \frac{v}{2} \right \rfloor \times 2$ 个 $k$ 连接到回文串的前后,此时如果 $k$ 还剩余一个,那么我们可以先记录到 $x$ 中。
如果 $k$ 中两个字母不同,那么我们要找到一个单词 $k'$,使得 $k'$ 中的两个字母与 $k$ 相反,即 $k' = k[1] + k[0]$。如果 $k'$ 存在,那么我们可以将 $\min(v, cnt[k'])$ 个 $k$ 连接到回文串的前后。
遍历结束后,如果 $x$ 不为空,那么我们还可以将一个单词连接到回文串的中间。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为 words
的长度。
1
2
3
4
5
6
7
8
9
10
11
12 | class Solution:
def longestPalindrome(self, words: List[str]) -> int:
cnt = Counter(words)
ans = x = 0
for k, v in cnt.items():
if k[0] == k[1]:
x += v & 1
ans += v // 2 * 2 * 2
else:
ans += min(v, cnt[k[::-1]]) * 2
ans += 2 if x else 0
return ans
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution {
public int longestPalindrome(String[] words) {
Map<String, Integer> cnt = new HashMap<>();
for (var w : words) {
cnt.put(w, cnt.getOrDefault(w, 0) + 1);
}
int ans = 0, x = 0;
for (var e : cnt.entrySet()) {
var k = e.getKey();
var rk = new StringBuilder(k).reverse().toString();
int v = e.getValue();
if (k.charAt(0) == k.charAt(1)) {
x += v & 1;
ans += v / 2 * 2 * 2;
} else {
ans += Math.min(v, cnt.getOrDefault(rk, 0)) * 2;
}
}
ans += x > 0 ? 2 : 0;
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Solution {
public:
int longestPalindrome(vector<string>& words) {
unordered_map<string, int> cnt;
for (auto& w : words) cnt[w]++;
int ans = 0, x = 0;
for (auto& [k, v] : cnt) {
string rk = k;
reverse(rk.begin(), rk.end());
if (k[0] == k[1]) {
x += v & 1;
ans += v / 2 * 2 * 2;
} else if (cnt.count(rk)) {
ans += min(v, cnt[rk]) * 2;
}
}
ans += x ? 2 : 0;
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | func longestPalindrome(words []string) int {
cnt := map[string]int{}
for _, w := range words {
cnt[w]++
}
ans, x := 0, 0
for k, v := range cnt {
if k[0] == k[1] {
x += v & 1
ans += v / 2 * 2 * 2
} else {
rk := string([]byte{k[1], k[0]})
if y, ok := cnt[rk]; ok {
ans += min(v, y) * 2
}
}
}
if x > 0 {
ans += 2
}
return ans
}
|