前后缀分解
题目
给你两个字符串 s 和 t 。
你可以从字符串 t 中删除任意数目的字符。
如果没有从字符串 t 中删除字符,那么得分为 0 ,否则:
令 left 为删除字符中的最小下标。
令 right 为删除字符中的最大下标。
字符串的得分为 right - left + 1 。
请你返回使 t 成为 s 子序列的最小得分。
一个字符串的 子序列 是从原字符串中删除一些字符后(也可以一个也不删除),剩余字符不改变顺序得到的字符串。(比方说 "ace" 是 "abcde" 的子序列,但是 "aec" 不是)。
示例
示例1:
输入:s = "abacaba", t = "bzaa"
输出:1
解释:这个例子中,我们删除下标 1 处的字符 "z" (下标从 0 开始)。
字符串 t 变为 "baa" ,它是字符串 "abacaba" 的子序列,得分为 1 - 1 + 1 = 1 。
1 是能得到的最小得分。
示例2:
输入:s = "cde", t = "xyz"
输出:3
解释:这个例子中,我们将下标为 0, 1 和 2 处的字符 "x" ,"y" 和 "z" 删除(下标从 0 开始)。
字符串变成 "" ,它是字符串 "cde" 的子序列,得分为 2 - 0 + 1 = 3 。
3 是能得到的最小得分。
策略
考察的知识点:字符串前后缀分解
[参考了灵神讲解]
-
注意到得分指的是 删除字符中的最大下标
right- 删除字符中的最小下标left+ 1, 那么在[left,right],我们删除一部分和删除全部得到的分数是一样的,那么贪心的去想,删的越多(剩的越少)就越可能成为子序列,那么就索性直接删除一个子字符串,问题就变成了从t中删除一个子字符串,使剩余的部分成为s的一个子序列 -
怎么找要删除的子串?
答:将s分割成两个部分,分别去匹配t的前缀和后缀,找匹配长度之和最大的一个方案

Code
C++ version
class Solution {
public:
int minimumScore(string s, string t) {
// 前后缀分解
int n = s.length(), m = t.length();
int suf[n + 1]; // # suf[i] = j 表示匹配后缀s[i:] 匹配了 t[j:]
// 为什么多开一个因为后面用到了suf[n]
// suf[n] = m, 表示i不拿字符出来,t也匹配不出
suf[n] = m;
// 倒叙枚举分割位置,求后缀匹配
for (int i = n - 1, j = m - 1; i >= 0; i--) {
if (j >= 0 && s[i] == t[j]) j--;
suf[i] = j + 1;
}
// 表示通过后缀匹配 去掉t[:suf[0]]
int res = suf[0];
// 正序枚举分割位置,求前缀匹配,并更新答案
for (int i = 0, j= 0; i < n; i++) {
if (j < m && s[i] == t[j]) {
j++;
if (suf[i + 1] - j >= 0)
res = min(res, suf[i + 1] - j);
}
}
return res;
}
};
Python version
class Solution:
def minimumScore(self, s: str, t: str) -> int:
# 前后缀分解
n, m = len(s), len(t)
suf = [m] * (n + 1) # suf[i] = j 表示匹配后缀s[i:] 匹配了 t[j:]
j = m - 1
# 倒叙枚举分割位置,求后缀匹配
for i in range(n - 1, -1, -1):
if j >= 0 and s[i] == t[j]:
j -= 1
suf[i] = j + 1
# 表示通过后缀匹配 去掉t[:suf[0]]
res = suf[0]
# 正序枚举分割位置,求前缀匹配,并更新答案
j = 0
for i in range(n):
if j < m and s[i] == t[j]:
j += 1
# 更新答案
if suf[i+1] - j >= 0:
res = min(res, suf[i+1] - j)
return res

浙公网安备 33010602011771号