步骤 1:理解题意
给定 无重复 单词列表 words,找出所有 不同的下标对 (i, j) ,使得 words[i] + words[j] 构成回文串。
注意:
- i 可以等于 j 吗?题目说"不同的索引对",且 words 无重复,故 i≠j。
- 结果只需返回下标对列表,顺序不限。
步骤 2:写出暴力/朴素解法
双重循环枚举所有 (i, j),拼接后 O(L) 判断是否回文,总复杂度 O(n²·L),n≤5000 会 TLE。
步骤 3:寻找冗余与优化点
核心观察:
对于拼接串 left + right 要成回文,
left的后缀 与right的前缀互为逆串;- 剩余部分自身必须是回文。
预处理:
- 把每个单词及其下标存入
HashMap<String,Integer>,O(1) 查逆串是否存在。 - 枚举每个单词
words[i],把它拆成prefix + suffix,共 L+1 种拆法:- 若
prefix是回文,且suffix的逆串存在于 words,则存在 j 使得(j, i)成对; - 若
suffix是回文,且prefix的逆串存在于 words,则存在 j 使得(i, j)成对。
- 若
单次检查 O(L),总复杂度 O(n·L²),n≤5000, L≤20 可过。
步骤 4:给出最优"快刷"版本
java
public List<List<Integer>> palindromePairs(String[] words) {
List<List<Integer>> ans = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < words.length; i++) map.put(words[i], i);
for (int i = 0; i < words.length; i++) {
String w = words[i];
int L = w.length();
for (int cut = 0; cut <= L; cut++) {
String prefix = w.substring(0, cut);
String suffix = w.substring(cut);
// 情况1:prefix 是回文,找 suffix 的逆
if (isPalindrome(prefix)) {
String rev = new StringBuilder(suffix).reverse().toString();
Integer j = map.get(rev);
if (j != null && j != i) {
ans.add(Arrays.asList(j, i));
}
}
// 情况2:suffix 是回文,找 prefix 的逆(避免重复拆空串)
if (cut != L && isPalindrome(suffix)) {
String rev = new StringBuilder(prefix).reverse().toString();
Integer j = map.get(rev);
if (j != null && j != i) {
ans.add(Arrays.asList(i, j));
}
}
}
}
return ans;
}
private boolean isPalindrome(String s) {
int l = 0, r = s.length() - 1;
while (l < r) if (s.charAt(l++) != s.charAt(r--)) return false;
return true;
}
- 时间:O(n·L²)
- 空间:O(n·L)(哈希表)
- 可直接 copy 通过 LintCode 775。