leetcode 5
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
思路:
反转字符串
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if(n < 2) return s;
// 反转s
string rev_s = "";
for(int i = n - 1; i >= 0; i--) rev_s += s[i];
// 声明dp数组,n+1维是因为状态转移方程里有[i-1][j-1]
int dp[n+1][n+1];
memset(dp, 0, sizeof(dp));
// 填dp数组
int maxx = 0, end = 0; // maxx是最长回文子串长度,end是对应的s中的index
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(s[i - 1] == rev_s[j - 1]) dp[i][j] = dp[i-1][j-1] + 1;
if(dp[i][j] > maxx && n - j + dp[i][j] == i) { // n - j + dp[i][j]是将s'中第j位的字符返回到s中对应的index
maxx = dp[i][j];
end = i - 1;
}
}
}
return s.substr(end - maxx + 1, maxx);
}
};
动态规划
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if(n < 2) return s;
// 声明dp数组,做了空间复杂度优化,原始dp[i][j]表示从s[i]到s[j]是否为回文子串
int dp[n];
memset(dp, 0, sizeof(dp));
int maxx = 1, end = 0;
for(int i = n - 1; i >= 0; i--) { // 从后往前遍历(因为需要dp[i+1][j-1])
for(int j = n - 1; j >= i; j--) { // 从后往前遍历(因为需要dp[i+1][j-1])
if(i == j) dp[j] = 1;
else if(i + 1 == j) {
dp[j] = (s[i] == s[j]);
if(dp[j] == 1 && maxx < 2) {
maxx = 2;
end = j;
}
}
else {
dp[j] = dp[j-1] && (s[i] == s[j]);
if(dp[j] == 1 && j - i + 1 > maxx) {
maxx = j - i + 1;
end = j;
}
}
}
}
return s.substr(end - maxx + 1, maxx);
}
};
中心扩展法
class Solution {
public:
int maxx = 0;
int l_max = 0;
void findLongest(string&s, int l, int r) {
int n = s.size();
while(l >= 0 && r < n && s[l] == s[r]) {
l--;
r++;
}
if(r - l - 1 > maxx) {
maxx = r - l - 1;
l_max = l + 1;
}
}
string longestPalindrome(string s) {
int n = s.size();
if(n < 2) return s;
for(int i = 0; i < n; i++) {
findLongest(s, i, i);
findLongest(s, i, i + 1);
}
return s.substr(s, l_max, maxx);
}
};
Manacher算法
class Solution {
public:
string longestPalindrome(string s) {
}
};
leetcode 131
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
思路: 题目要求所有可能的分割方案,因此想到使用dfs暴力搜索。
注意: 搜索的范围(for循环)是当前的左边界可能对应的回文右边界。
class Solution {
public:
vector<vector<string>> ans;
// 判断是否是回文串
bool isValid(string& s, int l, int i) {
while(l < i) {
if(s[l++] != s[i--]) return false;
}
return true;
}
// dfs搜索
void dfs(string& s, vector<string>& vec, int l) {
int n = s.size();
if(l > n - 1) { // 如果左区间溢出右边界,返回
ans.push_back(vec);
return;
}
for(int i = l; i < n; i++) { // 从当前左边界l搜索可能的回文右边界i
if(!isValid(s, l, i)) continue;
vec.push_back(s.substr(l, i - l + 1));
dfs(s, vec, i + 1);
vec.pop_back(); // 回退
}
}
vector<vector<string>> partition(string s) {
int n = s.size();
if(n == 0) return ans;
vector<string> vec;
dfs(s, vec, 0);
return ans;
}
};
leetcode 132
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。
思路: 因为求的是最值(最小分割次数),并且可以将问题划分为最优子问题,所以考虑使用动态规划来做。
注意: 因为之后要用min比较得到最小分割次数,所以初始化的时候将dp数组初始为当前可能的最大分割次数。
class Solution {
public:
// 判断是否是回文
bool isValid(string&s, int l, int r) {
while(l < r) {
if(s[l++] != s[r--]) return false;
}
return true;
}
int minCut(string s) {
int n = s.size();
if(n < 2) return 0;
int dp[n];
for(int i = 0; i < n; i++) dp[i] = i; // 初始化为最大分割次数
for(int i = 0; i < n; i++) {
if(isValid(s, 0, i)) dp[i] = 0; // 如果整个都是个回文串,dp[i]当然等于0
else {
for(int j = 1; j <= i; j++) { // 遍历以当前位置结尾的回文串
if(isValid(s, j, i)) {
dp[i] = min(dp[i], dp[j-1] + 1); // 寻找最小分割次数的组合
}
}
}
}
return dp[n-1];
}
};
leetcode 214
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。