Hi 大家好,我是张小猪。欢迎来到『宝宝也能看懂』系列之 leetcode 周赛题解。
这里是第 173 期的第 1 题,也是题目列表中的第 1332 题 -- 『删除回文子序列』
给你一个字符串 s
,它仅由字母 'a' 和 'b' 组成。每一次删除操作都可以从 s
中删除一个回文 子序列。
返回删除给定字符串中所有字符(字符串为空)的最小删除次数。
「子序列」定义:如果一个字符串可以通过删除原字符串某些字符而不改变原字符顺序得到,那么这个字符串就是原字符串的一个子序列。
「回文」定义:如果一个字符串向后和向前读是一致的,那么这个字符串就是一个回文。
示例 1:
输入:s = "ababa"
输出:1
解释:字符串本身就是回文序列,只需要删除一次。
示例 2:
输入:s = "abb"
输出:2
解释:"abb" -> "bb" -> "".
先删除回文子序列 "a",然后再删除 "bb"。
示例 3:
输入:s = "baabb"
输出:2
解释:"baabb" -> "b" -> "".
先删除回文子序列 "baab",然后再删除 "b"。
示例 4:
输入:s = ""
输出:0
提示:
0 <= s.length <= 1000
s
仅包含字母 'a' 和 'b'
EASY
题目的意思非常的简单,给定一个只由 'a' 和 'b' 这两种字母组成的字符串,需要返回把该字符串删空所需的最小次数。要求也只有一条,是每次删除的是一个回文子序列。
等等,what?excuse me?黑人问号脸...又是回文字符串,又是最小次数,还是 EASY 难度...吓得小猪赶紧揉了揉眼睛,在确认没看错之后赶紧吃了一袋薯片压压惊...
想了想,发现没有借口再吃第二袋薯片了,于是决定还是再看看题吧。发现题目中其实有一个很重要的信息,那就是每次可以被移除的是一个回文子序列。不是回文子字符串,不是回文子字符串,不是回文子字符串!重要的事情说三编。凑字数
为什么我会说这一个信息非常重要呢,重要到直接回归到了 EASY 的难度。下面我们举个例子吧:
现在我有一个原始字符串是 'aababbaabaabbabbaaabbabbbaabba'。那么它的子字符串是什么,就是截取其中连续的一串,例如 'babbaa'。而它的子序列是什么呢,就是不打乱顺序的从中随意取出字符,凑成一串,例如 'aaaa'。
相信看到这里,小伙伴们应该明白为什么说难度一下子降到 EASY 了吧。虽然题目有要求是回文子序列,可是如果我的子序列中只包含一种类型的字符,例如只包含 'a' 或者 'b',那无论长度是多少,都是回文字符串了。再然后,原始字符串只由 'a' 和 'b' 这两种字母组成。也就是说...其实我们最多只需要两次就可以删空整个原始字符串了。
是不是非常的鹅妹子嘤!哈哈哈哈,机智的小猪又有借口可以吃薯片啦 >.<
上面我们分析出了解题思路,以及最大的次数是 2。那么具体什么情况下是 0、1 和 2 呢。
- 0 的话非常直白,如果原始字符串本身为空,那么我们需要的次数就是 0。
- 1 的话意味着我们一次就删掉了所有内容,但我们只能删掉回文子序列,那么也就是说如果原始字符串本身就是一个回文字符串,那么我们需要的次数就是 1。
- 2 的话剩下的其他字符串就是需要两次即可,原理在上面的思路种已经分析过啦。
到了这一步,相信小伙伴们可以轻松的给出实现流程了吧。具体如下:
- 判断原始字符串是否为空,如果是则返回 0。
- 判断原始字符串是否为回文字符串,如果是则返回 1。
- 返回 2。
基于这个流程,我们可以实现类似下面的代码:
const removePalindromeSub = s => {
if (s.length === 0) return 0;
for (let left = 0, right = s.length - 1; left < right; ++left, --right) {
if (s[left] !== s[right]) return 2;
}
return 1;
};
当然我们也可以一行代码来实现,只不过判断回文的逻辑需要做一点变化。也就是通过把原始字符串反序然后和自身比较来判断是否是回文字符串。具体一行实现的代码如下:
const removePalindromeSub = s => s.length === 0 ? 0 : s.split('').reverse().join('') === s ? 1 : 2;
说实话最开始小猪是有被题目吓到。因为按照惯例第一题应该是个送分题鸭,怎么一上来就是回文字符串什么的。仔细看题之后才发现其中的玄机。正应征了,鲁迅曾经可能没说过,『看题不仔细,做题两行泪』。小伙伴们可不要学小猪这样为了吃薯片不好好看题鸭。
加油武汉,天佑中华