Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
1、问题描述
给定一个字符串 s ,返回 其重新排列组合后可能构成的所有回文字符串,并去除重复的组合 。
你可以按 任意顺序 返回答案。如果 s 不能形成任何回文排列时,则返回一个空列表。
示例 1:
输入: s = "aabb"
输出: ["abba", "baab"]
示例 2:
输入: s = "abc"
输出: []
提示:
1 <= s.length <= 16
s 仅由小写英文字母组成
2、思路分析
阅读完题目之后我们可以知道,题目要求我们做的是使用给出的字符串s进行重新排列,将重新排列中的回文串记录并输出。
这又是一道回文串排列的相关问题,我们仍然可以用一样的套路来进行解题,将字符串划分为两半,我们只需要计算前半部分,后半部分便可以根据回文串的性质补全,我们可以使用递归深搜的方法来找出全部的回文排列组合串具体做法如下:
- (1)记录各字符出现次数
const sMap = {};
for(let i = 0; i < s.length; i++){
sMap[s[i]] ? sMap[s[i]]++ : sMap[s[i]] = 1;
}
- (2)判断是否有解 单数个字符最多只能有一个,否则本题则无解。
let singe = 0;
for(let key in sMap) sMap[key] % 2 == 1 ? singe++ : '';
if(singe > 1) return [];
- (3)计算前半部分字符长度
const preLen = Math.ceil(s.length / 2);
- (4)递归生成字符排列
奇数字符只能放在中间,即
sMap[key] == 1 && str.length == preLen - 1
let greateStr = function(str = ''){
if(str.length == preLen){
let len = preLen;
if(s.length % 2 === 1) len--;
while(len--){
str += str[len];
}
res.push(str);
return;
}
for(let key in sMap){
if(sMap[key] == 1 && str.length == preLen - 1){
sMap[key]--;
greateStr(str + key);
sMap[key]++;
}else if(sMap[key] >= 2){
sMap[key] -= 2;
greateStr(str + key);
sMap[key] += 2;
}
}
}
3、AC代码
/**
* @param {string} s
* @return {string[]}
*/
var generatePalindromes = function(s) {
const sMap = {};
for(let i = 0; i < s.length; i++){
sMap[s[i]] ? sMap[s[i]]++ : sMap[s[i]] = 1;
}
let singe = 0;
for(let key in sMap) sMap[key] % 2 == 1 ? singe++ : '';
if(singe > 1) return [];
let res = [];
const preLen = Math.ceil(s.length / 2);
let greateStr = function(str = ''){
if(str.length == preLen){
let len = preLen;
if(s.length % 2 === 1) len--;
while(len--){
str += str[len];
}
res.push(str);
return;
}
for(let key in sMap){
if(sMap[key] == 1 && str.length == preLen - 1){
sMap[key]--;
greateStr(str + key);
sMap[key]++;
}else if(sMap[key] >= 2){
sMap[key] -= 2;
greateStr(str + key);
sMap[key] += 2;
}
}
}
greateStr('');
return res;
};
4、总结
算法其实很多时候也是有模板和套路的,只有刷了足够多的题目之后,我们才能够将眼前的题目和之前做过的题目关联起来,并能快速的找出解题的模板和套路。