通过从末尾删除字符并在任意位置重新插入,将字符串A 转换为 B
给定两个互为字谜的字符串A 和 B,任务是尽可能以最少的操作将 A 转换为 B。操作被定义为删除 A 中的第一个或最后一个字符并将其插入回字符串中的任何位置。
例子:
Input: A = “edacb”, B = “abcde”
Output : 3
Explanation : The three operations are:
- We can remove “b” and insert it after “a” to get “edabc”
- We can remove “e” and insert it after “c” to get “dabce”
- We can remove “d” and insert it after “c” to get “abcde”
Input : A = “xyz”, B = “xyz”
Output : 0
Explanation : String A is equal to String B so no operation is required.
方法:该方法基于动态规划和最长公共子序列(LCS)
直觉:
- 不是通过将字符从 A 的末端移动到内部来从 A 形成 B,我们将尝试通过将字符从 B 的内部移动到它的末端来从 B 形成 A。很明显,这个问题是等价的。
- 现在,在多次执行此反向操作之后,我们将得到一个结果字符串B' ,我们可以将其拆分为三个部分:
B' = 前缀 + 核心 + 后缀
在哪里- 核心由不动的字符组成,并且
- prefix / suffix 是分别移到开头或结尾的字符。
- 请注意,一旦我们移动了一个字符,再次移动它总是次优的。
由于我们可以自由选择移动字符的顺序,所以我们应该选择从内向外移动前缀/后缀的字符。
那么,以这种方式从 B 形成 B' 的成本是 len(prefix) + len(suffix)。 - 核心是 B 的子序列:它仅由 B 中未移动的字符按原始顺序组成。确实,这是双向的——我们可以将 B 的任何子序列变成一个核心,并在它周围任意排列剩余的字母。
- 现在我们的问题是找到一种方法将 A 分解为这三个部分:
- 最小化 len(prefix) + len(suffix)。
- 最大化 len(core) (未触及字符的数量)。
- 这可以重新表述为找到 A 的最大长度子串,它是 B 的子序列。
算法:这个问题可以用动态规划来解决,就像经典的 LCS(最长公共子序列)问题一样。有关精确的 DP 状态,请参阅代码中的注释。与 LCS 的主要区别在于我们不能在 dp[i][j] 中包含 dp[i-1][j],因为这会破坏 A 子串的连续性。
以下是上述方法的实现:
C++
// C++ implementation of the above approach
#include
using namespace std;
// Function to find min operations
int minOperations(string A, string B)
{
// dp[i][j] = length of longest
//(contiguous) suffix of A[0..i]
// that is a subsequence of B[0..j]
int dp[1001][1001];
// r = maximum value over all
// dp[i][j] computed so far
int r = 0;
for (int i = 0; i <= A.size(); ++i) {
for (int j = 0; j <= B.size(); ++j) {
dp[i][j] = 0;
if (i && j) {
// any suffix of A[0..i]
// which is a subsequence of B[0..j]
// is also a subsequence of B[0..j-1]...
dp[i][j] = dp[i][j - 1];
// or, if last character matches (i.e.
// A[i-1] == B[j-1]), and then the rest
// of the suffix is a suffix of
// A[0..i-1] and a subsequence of B[j-1]
if (A[i - 1] == B[j - 1]) {
dp[i][j] = max(
dp[i][j],
1 + dp[i - 1][j - 1]);
r = max(r, dp[i][j]);
}
}
}
}
// r = the length of the
// longest (contiguous) substring
// of A that is a subsequence of B
return A.size() - r;
}
// Driver code
int main()
{
string A = "edacb";
string B = "abcde";
cout << minOperations(A, B);
return 0;
}
Java
// Java implementation of the above approach
import java.util.*;
public class GFG {
// Function to find min operations
static int minOperations(String A, String B)
{
// dp[i][j] = length of longest
//(contiguous) suffix of A[0..i]
// that is a subsequence of B[0..j]
int[][] dp = new int[1001][1001];
// r = maximum value over all
// dp[i][j] computed so far
int r = 0;
for (int i = 0; i <= A.length(); ++i) {
for (int j = 0; j <= B.length(); ++j) {
dp[i][j] = 0;
if (i > 0 && j > 0) {
// any suffix of A[0..i]
// which is a subsequence of B[0..j]
// is also a subsequence of B[0..j-1]...
dp[i][j] = dp[i][j - 1];
// or, if last character matches (i.e.
// A[i-1] == B[j-1]), and then the rest
// of the suffix is a suffix of
// A[0..i-1] and a subsequence of B[j-1]
if (A.charAt(i - 1)
== B.charAt(j - 1)) {
dp[i][j] = Math.max(
dp[i][j], 1 + dp[i - 1][j - 1]);
r = Math.max(r, dp[i][j]);
}
}
}
}
// r = the length of the
// longest (contiguous) substring
// of A that is a subsequence of B
return A.length() - r;
}
// Driver code
public static void main(String args[])
{
String A = "edacb";
String B = "abcde";
System.out.println(minOperations(A, B));
}
}
// This code is contributed by Smim Hossain Mondal.
Python3
输出
3
时间复杂度:
辅助空间: