题目描述
给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?
题目解析
当str2长度远小于str1时
- 生成s2的所有子序列:每个位置由两种选择—要、不要
- 然后将所有子序列根据长度排序,长度大的在前面
- 考察哪个子序列跟s1的某个字串相等(kmp)。
- 假设s2长度为9
- 长度为9的子序列只有1个,然后kmp发现返回-1,它不是s1的字串
- 长度为8的子序列由若干个,它们都不是s1的字串
- 长度为7的子序列时有一个是s1的字串,那么删除两个字符就可以了
- 下面就不用去验证了
复杂度分析:
- 假设s1的长度为N,s2的长度M
- 第一步:生成所有子序列,子序列个数 2 M 2^M 2M,每个子序列都要生成字符串,所以代价为 O ( 2 M ∗ M ) O(2^M * M) O(2M∗M)
- 第二步:对每个子序列都要做KMP,代价为 O ( 2 M ∗ N ) O(2^M * N) O(2M∗N)
- 如果M不大,那么可以这样做
class Solution {
void generatorSubs(std::string &s, int idx, std::string path, std::vector<std::string>&s2Subs){
if(idx == s.size()){
s2Subs.emplace_back(path);
return;
}
generatorSubs(s, idx + 1, path, s2Subs);
generatorSubs(s, idx + 1, path + s[idx], s2Subs);
}
public:
int minCost(std::string s1, std::string s2){
std::vector<std::string> s2Subs;
generatorSubs(s2, 0, "", s2Subs);
std::sort(s2Subs.begin(), s2Subs.end(), [](std::string left, std::string right){
return left.size() < right.size();
});
for(auto &str : s2Subs){
// 待完成.................
}
return s2.length();
}
};
当s2的长度比较大时
因为是s2要删除字符成为s1的字串,因此我们把所有s1的字串类出来,看s2能不能通过删除的方式到s1的每一个字串
用编辑距离优化。
实现:
class Solution {
// x字符串只通过删除的方式,变到y字符串, 返回至少要删几个字符
int onlyDelete(std::string x, std::string y){
int M = x.size(), N = y.size();
// if(M < N){
// return INT32_MAX;
// }
std::vector<std::vector<int>> dp(M + 1, std::vector<int>(N + 1, INT32_MAX));
dp[0][0] = 0;
for (int i = 1; i <= M; ++i) {
dp[i][0] = i;
}
// 对于当前字符,有两种决策:删/不删
// + 当x[i] == y[j]:只需要前i-1个字符变成前j-1个字符就可以
// + 当x[i] != y[j]:只需要前i-1个字符变成前j个字符,然后把x[i]删除就好了
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= std::min(i, N); ++j) {
if(dp[i - 1][j] != INT32_MAX){
dp[i][j] = dp[i - 1][j] + 1;
}
if (x[i - 1] == y[j - 1] && dp[i - 1][j - 1] != INT32_MAX) {
dp[i][j] = std::min(dp[i - 1][j - 1], dp[i][j]);
}
}
}
return dp[M][N];
}
public:
int minCost2(std::string s1, std::string s2){
int ans = INT32_MAX;
for (int start = 0; start < s1.length(); start++) {
for (int end = start ; end < s1.length(); end++) {
ans = std::min(ans, onlyDelete(s2, s1.substr(start, end - start + 1)));
}
}
return ans == INT32_MAX ? s2.length() : ans;
}
};
复杂度分析:
- 假设s1的长度为N,s2的长度M
- s1字串个数为 O ( N 2 ) O(N^2) O(N2)(因为每个字符都有两种选择)
- s2对每一个s1的字串都有一张二维表,但是一共编辑距离问题
- 所以一共有 N 2 N^2 N2张二维表,每张表的大小 M ∗ N M * N M∗N,是总的复杂度的是 O ( N 3 ∗ M ) O(N^3 * M) O(N3∗M)
- 因为上面的解法是 O ( 2 M ∗ N ) O(2^M * N) O(2M∗N),所以张M小时选上面,M大时选本方法
优化
怎么只通过删除s2将s2变成s1的字串呢?
- 首先要确保 s 2. l e n g t h > = s 1. l e n g t h s2.length >= s1.length s2.length>=s1.length
- 标准的编辑距离有三种行为:插入、删除、替换
- 在这里可以认为插入、替换无穷大,删除行为代价为1
对数器