This commit is contained in:
parent
50bf6b650b
commit
0b3383ad03
36
include/s0005_longest_palindromic_substring.hpp
Normal file
36
include/s0005_longest_palindromic_substring.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef S0005_LONGEST_PALINDROMIC_SUBSTRING
|
||||||
|
#define S0005_LONGEST_PALINDROMIC_SUBSTRING
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class Solution1 {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Longest Palindromic Substring
|
||||||
|
*
|
||||||
|
* Given a string s, return the longest palindromic substring in s.
|
||||||
|
*
|
||||||
|
* @param s the given string
|
||||||
|
* @return longest palindromic substring
|
||||||
|
*/
|
||||||
|
string longestPalindrome(string s);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Solution2 {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Longest Palindromic Substring
|
||||||
|
*
|
||||||
|
* Given a string s, return the longest palindromic substring in s.
|
||||||
|
*
|
||||||
|
* @param s the given string
|
||||||
|
* @return longest palindromic substring
|
||||||
|
*/
|
||||||
|
string longestPalindrome(string s);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
139
src/s0005_longest_palindromic_substring.cpp
Normal file
139
src/s0005_longest_palindromic_substring.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#include "s0005_longest_palindromic_substring.hpp"
|
||||||
|
|
||||||
|
// 思路一:字符串翻转 + 动态规划
|
||||||
|
// 看到回文串就要想到字符串翻转。
|
||||||
|
// 字符串翻转之后问题就可以描述成找到 s 和 reverse(s) 的最长公共子字符串
|
||||||
|
// 再看一个例子,S="abc435cba",S="abc534cba",最长公共子串是 "abc" 和
|
||||||
|
// "cba",但很明显这两个字符串都不是回文串。
|
||||||
|
// 所以我们求出最长公共子串后,并不一定是回文串,我们还需要判断该字符串倒置前的下标和当前的字符串下标是不是匹配。
|
||||||
|
|
||||||
|
// 思路二:直接动态规划
|
||||||
|
// dp[i][j] 表示起始为 i ,终点为 j 的子字符串是否为回文字符串
|
||||||
|
// 那么有:
|
||||||
|
// if s[i] == s[j]:
|
||||||
|
// dp[i][j] = dp[i - 1][j - 1]
|
||||||
|
// else:
|
||||||
|
// dp[i][j] = false
|
||||||
|
// 把 dp 这张二维数组的表全部填好,然后在里面找最长的子字符串。
|
||||||
|
// 时间复杂度:O(n^2) 因为总共有 n^2 个状态,计算每个状态需要的时间为 O(1)
|
||||||
|
// 空间复杂度:O(n^2) 因为总共需要存储 n^2 个状态。
|
||||||
|
string Solution1::longestPalindrome(string s) {
|
||||||
|
int len = s.length();
|
||||||
|
vector<vector<int>> dp(len, vector<int>(len));
|
||||||
|
|
||||||
|
if (len < 2) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// L 表示子字符串长度
|
||||||
|
// L = 1, true
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
dp[i][i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// L = 2
|
||||||
|
// if l == r, dp = true
|
||||||
|
// else, dp = false
|
||||||
|
for (int i = 0; i < len - 1; i++) {
|
||||||
|
if (s[i] == s[i + 1]) {
|
||||||
|
dp[i][i + 1] = true;
|
||||||
|
} else {
|
||||||
|
dp[i][i + 1] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// L >= 3
|
||||||
|
// if l == r, dp = dp_prev
|
||||||
|
// else, dp = false
|
||||||
|
for (int L = 3; L <= len; L++) {
|
||||||
|
for (int i = 0; i + L - 1 < len; i++) {
|
||||||
|
if (s[i] == s[i + L - 1]) {
|
||||||
|
dp[i][i + L - 1] = dp[i + 1][i + L - 2];
|
||||||
|
} else {
|
||||||
|
dp[i][i + L - 1] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find max len
|
||||||
|
int maxLen = 1;
|
||||||
|
int beginIndex = 0;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
for (int j = i; j < len; j++) {
|
||||||
|
if (dp[i][j]) {
|
||||||
|
if (j - i + 1 > maxLen) {
|
||||||
|
maxLen = j - i + 1;
|
||||||
|
beginIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.substr(beginIndex, maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 思路三:中心扩散算法
|
||||||
|
// 每个回文字符串都有一个“回文中心”
|
||||||
|
// 这个回文中心要么是一个长度为 1 的字符,要么是一个长度为 2
|
||||||
|
// 的字符串(这个字符串的两个字符都相等)
|
||||||
|
// “回文中心”实际上是两种边界情况,也就是说每个可能的结果都是由边界情况扩展开来的
|
||||||
|
// 因此我们可以遍历每个边界情况,对它进行扩展
|
||||||
|
|
||||||
|
typedef struct ResultStruct {
|
||||||
|
int left1; // 边界情况为长度为 1 的字符时,扩展完成后的左边界
|
||||||
|
int right1; // 边界情况为长度为 1 的字符时,扩展完成后的右边界
|
||||||
|
int left2; // 边界情况为长度为 2 的字符串时,扩展完成后的左边界
|
||||||
|
int right2; // 边界情况为长度为 2 的字符串时,扩展完成后的右边界
|
||||||
|
} Result;
|
||||||
|
|
||||||
|
// 输入为字符串和边界情况的那一点
|
||||||
|
// 返回值包含为扩展的结果
|
||||||
|
Result expand(string s, int i) {
|
||||||
|
int len = s.length();
|
||||||
|
|
||||||
|
if (i == len - 1) {
|
||||||
|
return Result{i, i, i, i};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边界情况为长度为 1 的字符时
|
||||||
|
int left1, right1;
|
||||||
|
for (int x = 0; i + x < len && i - x >= 0; x++) {
|
||||||
|
if (s[i - x] == s[i + x]) {
|
||||||
|
left1 = i - x;
|
||||||
|
right1 = i + x;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边界情况为长度为 2 的字符串时
|
||||||
|
int left2, right2;
|
||||||
|
for (int x = 0; i + x + 1 < len && i - x >= 0; x++) {
|
||||||
|
if (s[i - x] == s[i + x + 1]) {
|
||||||
|
left2 = i - x;
|
||||||
|
right2 = i + x + 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result{left1, right1, left2, right2};
|
||||||
|
}
|
||||||
|
|
||||||
|
string Solution2::longestPalindrome(string s) {
|
||||||
|
int len = s.length();
|
||||||
|
int left = 0, right = 0;
|
||||||
|
Result result;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
result = expand(s, i);
|
||||||
|
if (result.right1 - result.left1 > right - left) {
|
||||||
|
right = result.right1;
|
||||||
|
left = result.left1;
|
||||||
|
}
|
||||||
|
if (result.right2 - result.left2 > right - left) {
|
||||||
|
right = result.right2;
|
||||||
|
left = result.left2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.substr(left, right - left + 1);
|
||||||
|
}
|
22
tests/s0005_longest_palindromic_substring.cpp
Normal file
22
tests/s0005_longest_palindromic_substring.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "s0005_longest_palindromic_substring.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Problem5, Case1) {
|
||||||
|
std::string s("babad");
|
||||||
|
std::string r1("bab");
|
||||||
|
std::string r2("aba");
|
||||||
|
Solution1 solution1;
|
||||||
|
Solution2 solution2;
|
||||||
|
EXPECT_TRUE(solution1.longestPalindrome(s) == r1 || solution1.longestPalindrome(s) == r2);
|
||||||
|
EXPECT_TRUE(solution2.longestPalindrome(s) == r1 || solution2.longestPalindrome(s) == r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Problem5, Case2) {
|
||||||
|
std::string s("cbbd");
|
||||||
|
std::string r("bb");
|
||||||
|
Solution1 solution1;
|
||||||
|
Solution2 solution2;
|
||||||
|
EXPECT_EQ(r.compare(solution1.longestPalindrome(s)), 0);
|
||||||
|
EXPECT_EQ(r.compare(solution2.longestPalindrome(s)), 0);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user