题目:[1]
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
输入: "aacecaaa"
输出: "aaacecaaa"
输入: "abcd"
输出: "dcbabcd"
思路
最直观想到的思路就是找到从 s 首位开始的最长回文字符串,再讲不是该串的部分颠倒拼接到 s 头部就得到了需要的结果
其中校验字符串是否为子串在:20200610:回文数 (难度:简单)中就用到过
/**
* @param {string} s
* @return {string}
*/
var shortestPalindrome = function (s) {
let len = s.length
// 从长到短求最长前缀回文字符
for (let i = len; i >= 0; i--) {
let _result = s.substring(0, i)
if (check(_result)) {
return Array.from(s.substring(i, len)).reverse().join('') + s
}
}
// 校验是否为回文字符串
function check(x) {
let str = '' + x
return Array.from(str).reverse().join('') === str
}
}
提交后会发现 119 / 120 个通过测试用例,存在 2 个测试用例没有通过
抛砖引玉
换种思路
在20200823: 重复的子字符串 (难度:简单)[2]中就用到了 KMP 算法去校验一个模式串是否匹配另外一个匹配串
KMP算法
/**
* @param {string} s
* @return {string}
*/
var shortestPalindrome = function (s) {
let len = s.length,
str = s.split('').reverse().join('')
// 正反字符匹配求最长匹配位数
function kmp(query, pattern) {
let fail = Array(len).fill(-1)
// 最长公共前缀,不存在公共前缀填充-1
for (let i = 1; i < len; ++i) {
let j = fail[i - 1]
while (j != -1 && pattern[j + 1] != pattern[i]) {
j = fail[j]
}
if (pattern[j + 1] == pattern[i]) {
fail[i] = j + 1
}
}
// 校验 match记录匹配的位置
let match = -1
for (let i = 0; i < len; ++i) {
// 如果当前不匹配匹配,指针跳跃到前缀位置开始匹配
while (match != -1 && pattern[match + 1] != query[i]) {
match = fail[match]
}
// 如果当前位匹配,逐个向后匹配
if (pattern[match + 1] == query[i]) {
++match
}
}
return match
}
let num = kmp(str, s),
add = num == len - 1 ? '' : s.substring(num + 1)
return Array.from(add).reverse().join('') + s
}
[1]
题目:: https://leetcode-cn.com/problems/shortest-palindrome/
[2]
20200823: 重复的子字符串 (难度:简单):https://krdxst.gitee.io/oneday_oneleetcode/leetcode/202008/20200823.html