(关注数据结构和算法,了解更多新知识)
在牛客网上看到一网友发文称:25届已经回暖了,我还以为是真的,打开一看,吓我一跳,华为的研发工程师日薪都这么高了吗。很明显这是招聘软件出bug了,或者是职位发布的时候填错了。一网友回复:这不是回暖了,这是直接给干中暑了。
--------------下面是今天的算法题--------------
来看下今天的算法题,这题是LeetCode的第132题:分割回文串 II。
问题描述
来源:LeetCode第132题
难度:困难
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。返回符合要求的 最少分割次数 。
示例1:
输入:s = "aab"
输出:1
解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
示例2:
输入:s = "a"
输出:0
1 <= s.length <= 2000
s 仅由小写英文字母组成
问题分析
这题是让把字符串分割成回文子串,并且所需要的分割次数最少。我们可以使用动态规划解决,定义dp[i]表示前 i 个字符分割的最小次数,那么最后的结果就是dp[n-1],n是字符串的长度。
对于第 i 个字符,我们首先判断子串s[0,i]是否是回文串,如果是回文串,则不需要分割,所以dp[i]=0。
如果子串s[0,i]不是回文串,就要分割,只需要在这个字符串中找到一个下标 j ,判断子串s[j,i]是否是回文串,如果是回文串,我们可以把字符串s[0,i]分割成s[0,j-1]和s[j,i],所以可以得到dp[i]=dp[j-1]+1,表示进行了一次分割。
这里 j 的下标我们可以从 0 到 i 一个个枚举,只需要保存最小值即可,也就是:
dp[i] = min(dp[i], dp[j - 1] + 1);
判断子串是否是回文串我们可以提前计算,思路可以参考最长回文子串。
JAVA:
public int minCut(String s) {
int length = s.length();
int[] dp = new int[length];
// 判断子串[i…j]是否是回文串
boolean[][] palindrome = new boolean[length][length];
for (int j = 0; j < length; j++) {
for (int i = 0; i <= j; i++) {
// 如果i和j指向的字符不一样,那么dp[i][j]就不能构成回文字符串。
if (s.charAt(i) != s.charAt(j))
continue;
palindrome[i][j] = j - i <= 2 || palindrome[i + 1][j - 1];
}
}
// 字符串s的回文子串最大也只能是字符串的长度,这里都默认初始化为最大值。
Arrays.fill(dp, length);
for (int i = 0; i < length; i++) {
// 如果子串s[0,i]本身就是回文的,就不需要分隔。
if (palindrome[0][i]) {
dp[i] = 0;
} else {
// 否则就要分隔,找出最小的分隔方案
for (int j = 0; j <= i; ++j) {
if (palindrome[j][i])
dp[i] = Math.min(dp[i], dp[j - 1] + 1);
}
}
}
return dp[length - 1];
}
C++:
public:
int minCut(string s) {
int length = s.size();
// 字符串s的回文子串最大也只能是字符串的长度,这里都默认初始化为最大值。
vector<int> dp(length, length);
// 判断子串[i…j]是否是回文串
vector<vector<bool>> palindrome(length, vector<bool>(length, false));
for (int j = 0; j < length; j++) {
for (int i = 0; i <= j; i++) {
// 如果i和j指向的字符不一样,那么dp[i][j]就不能构成回文字符串。
if (s[i] != s[j])
continue;
palindrome[i][j] = j - i <= 2 || palindrome[i + 1][j - 1];
}
}
for (int i = 0; i < length; i++) {
// 如果子串s[0,i]本身就是回文的,就不需要分隔。
if (palindrome[0][i]) {
dp[i] = 0;
} else {
// 否则就要分隔,找出最小的分隔方案
for (int j = 0; j <= i; ++j) {
if (palindrome[j][i])
dp[i] = min(dp[i], dp[j - 1] + 1);
}
}
}
return dp[length - 1];
}
C:
int min(int a, int b) {
return a > b ? b : a;
}
int minCut(char* s) {
int length = strlen(s);
int dp[length];
// 判断子串[i…j]是否是回文串
bool palindrome[length][length];
memset(palindrome,0,sizeof(palindrome));
for (int j = 0; j < length; j++) {
for (int i = 0; i <= j; i++) {
// 如果i和j指向的字符不一样,那么dp[i][j]就不能构成回文字符串。
if (s[i] != s[j])
continue;
palindrome[i][j] = j - i <= 2 || palindrome[i + 1][j - 1];
}
}
// 字符串s的回文子串最大也只能是字符串的长度,这里都默认初始化为最大值。
for(int i = 0; i < length; i++)
dp[i] = length; // 初始化数组元素为1到5
for (int i = 0; i < length; i++) {
// 如果子串s[0,i]本身就是回文的,就不需要分隔。
if (palindrome[0][i]) {
dp[i] = 0;
} else {
// 否则就要分隔,找出最小的分隔方案
for (int j = 0; j <= i; ++j) {
if (palindrome[j][i])
dp[i] = min(dp[i], dp[j - 1] + 1);
}
}
}
return dp[length - 1];
}
Python:
def minCut(self, s: str) -> int:
length = len(s)
dp = [length] * length
# 判断子串[i…j]是否是回文串
palindrome = [[False] * length for _ in range(length)]
for j in range(length):
for i in range(j + 1):
# 如果i和j指向的字符不一样,那么dp[i][j]就不能构成回文字符串。
if s[i] != s[j]:
continue;
palindrome[i][j] = j - i <= 2 or palindrome[i + 1][j - 1];
for i in range(length):
# 如果子串s[0, i]本身就是回文的,就不需要分隔。
if palindrome[0][i]:
dp[i] = 0;
else:
# 否则就要分隔,找出最小的分隔方案
for j in range(i + 1):
if palindrome[j][i]:
dp[i] = min(dp[i], dp[j - 1] + 1);
return dp[length - 1];
笔者简介
博哥,真名:王一博,毕业十多年,《算法秘籍》作者,专注于数据结构和算法的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解800多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以下载我整理的1000多页的PDF算法文档。