一、题目介绍
给定一组唯一的单词, 找出所有不同 的索引对(i, j),使得列表中的两个单词, words[i] + words[j] ,可拼接成回文串。
示例 1:
输入: ["abcd","dcba","lls","s","sssll"]
输出: [[0,1],[1,0],[3,2],[2,4]]
解释: 可拼接成的回文串为 ["dcbaabcd","abcddcba","slls","llssssll"]
示例 2:
输入: ["bat","tab","cat"]
输出: [[0,1],[1,0]]
解释: 可拼接成的回文串为 ["battab","tabbat"]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-pairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、 解题思路
在数组中判断某一字符串能否与其他字符串组成回文对,主要分为以下几种情况:
假设当前字符串为s1
(1)如果s1本身为回文串,可以和空字符串组成回文对。
(2)如果s1反转之后,可以在数组中找到相同的字符串,则可以组成回文对,注意反转之后不能是自己(避免本身为回文串的情况)。
(3)如果s1可以找到一个分界点,分界点前的字符串为回文串,分界点后的字符串反转之后可以在数组中找到,则可以组成回文对。
(4)如果s1可以找到一个分界点,分界点后的字符串为回文串,分界点前的字符串反转之后可以在数组中找到,则可以组成回文对。
除了需要考虑以上内容之外,需要找到一种数据结构,能够快速查找某一字符串是否存在。
本文尝试了两种数据结构,第一种:HashMap(参考大佬);第二种:Trie树。
三、解题代码
(1)HashMap
class Solution {
public:
bool ispalindrome(string s)
{
if(s.empty()) return true;
for(int i=0;i<s.size()-1-i;i++)
if(s[i]!=s[s.size()-1-i])
return false;
return true;
}
vector<vector<int>> palindromePairs(vector<string>& words) {
vector<vector<int>>res;
unordered_set<int>s;
unordered_map<string,int>mp;
for(int i=0;i<words.size();i++)
{
mp[words[i]]=i;
s.insert(words[i].size());
}
for(int i=0;i<words.size();i++)
{
//将字符串反转在mp中查找是否存在,同时确保不能找到自己
string w=words[i];
reverse(w.begin(),w.end());
if(mp.find(w)!=mp.end()&&mp[w]!=i)
{
vector<int>cur;
cur.push_back(i);
cur.push_back(mp[w]);
res.push_back(cur);
}
//判断字符串的前缀或者后缀是不是回文串,回文串本身作为自己的前缀和后缀可以与空字符组组成回文对
for(int j=words[i].size()-1;j>=0;j--)
{
//首先判断 要查找的字符串长度在set中是否存在,不存在可直接pass,剪枝
if(s.find(j)==s.end())
continue;
string w1=words[i].substr(0,j);
if(ispalindrome(words[i].substr(j))) //判断后缀
{
reverse(w1.begin(),w1.end());
if(mp.find(w1)!=mp.end())
{
vector<int>cur;
cur.push_back(i);
cur.push_back(mp[w1]);
res.push_back(cur);
}
}
string w2=words[i].substr(words[i].size()-j);
if(ispalindrome(words[i].substr(0,words[i].size()-j))) //判断前缀
{
reverse(w2.begin(),w2.end());
if(mp.find(w2)!=mp.end()){
vector<int>cur;
cur.push_back(mp[w2]);
cur.push_back(i);
res.push_back(cur);
}
}
}
}
return res;
}
};
(2)Trie
struct Node
{
bool isword;
int pos;
unordered_map<char, Node*> mp;
Node()
{
isword = false;
}
};
class Trie
{
private:
Node* root;
public:
Trie()
{
root = new Node();
}
void insert(string word, int n)
{
Node* p = root;
for(int i = 0; i < word.size(); ++i)
{
if(p->mp[word[i]] == NULL)
p->mp[word[i]] = new Node();
p = p->mp[word[i]];
}
p->isword = true;
p->pos = n;
}
int search(string word)
{
Node* p = root;
for(int i = 0; i < word.size(); ++i)
{
if(p->mp[word[i]] == NULL)
return -1;
p = p->mp[word[i]];
}
if(p->isword)
return p->pos;
return -1;
}
};
class Solution {
public:
bool isPalindrome(string word)
{
int n = word.size();
for(int i = 0; i < n/2; ++i)
{
if(word[i] != word[n-1-i])
return false;
}
return true;
}
vector<vector<int>> palindromePairs(vector<string>& words) {
Trie t;
unordered_map<string, int> mp;
unordered_set<int> s;
//将所有单词添加到前缀树中
for(int i = 0; i < words.size(); ++i)
{
t.insert(words[i], i);
s.insert(words[i].size());
}
vector<vector<int>> res;
int rtn = -1;
for(int i = 0; i < words.size(); ++i)
{
//将当前字符串反转后在前缀树中查找 (1)
string str = words[i];
reverse(str.begin(), str.end());
rtn = t.search(str);
if(rtn != -1 && rtn != i) //规避本身为回文串,反转之后找到自身的情况
{
vector<int> vec;
vec.push_back(i);
vec.push_back(rtn);
res.push_back(vec);
}
//确定当前字符串的前缀或者是否为回文串, 将前缀和后缀的范围扩展到可以包括字符串本身
for(int j = words[i].size()-1; j >= 0; --j)
{
//剪枝 j代表将要查找的字符串长度
if(s.find(j) == s.end())
continue;
string str2 = words[i].substr(0, j);
if(isPalindrome(words[i].substr(j))) //判断后缀子串
{
reverse(str2.begin(), str2.end());
rtn = t.search(str2);
if(rtn != -1)
{
vector<int> vec;
vec.push_back(i);
vec.push_back(rtn);
res.push_back(vec);
}
}
string str1 = words[i].substr(words[i].size()-j);
if(isPalindrome(words[i].substr(0, words[i].size()-j))) //判断前缀子串
{
reverse(str1.begin(), str1.end());
int rtn = t.search(str1);
if(rtn != -1)
{
vector<int> vec;
vec.push_back(rtn);
vec.push_back(i);
res.push_back(vec);
}
}
}
}
return res;
}
};
四、解题结果
(1)HashMap
(2)Trie树