我定义以下函数:F(d, i, j) = 由前缀组成的字符串可能的最小重复次数arr1
长度为 i 且前缀为arr2
长度为 j,后跟第 i (d=0) 或第 j (d=1) 个符号arr[d]
。因此 F(d, i, j) 对应于长度为 i+j+1 的字符串。
如果您熟悉编辑距离的计算方式,请将其视为我们不是为网格的顶点分配分数,而是为边缘分配分数,其中d
表示它是水平边缘还是垂直边缘。这给了我们一个单一符号的“记忆”,所以我们可以检测重复。
以下 C++ 代码计算最小重复次数并以二次方时间打印相应的字符串:
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <limits.h>
char A[32], B[32], C[64];
int score[2][32][32];
void print_result(int d, int i, int j)
{
char c = d ? B[j] : A[i];
int s0 = i > 0 ? score[0][i-1][j] + (A[i-1] == c) : INT_MAX;
int s1 = j > 0 ? score[1][i][j-1] + (B[j-1] == c) : INT_MAX;
if(s0 <= s1 && i > 0)
print_result(0, i-1, j);
else if(j > 0)
print_result(1, i, j-1);
printf("%c", c);
}
void print_result(int i, int j)
{
if(score[0][i-1][j] < score[1][i][j-1])
print_result(0, i-1, j);
else
print_result(1, i, j-1);
}
int main()
{
fgets(A, sizeof(A), stdin);
fgets(B, sizeof(B), stdin);
int m = strlen(A) - 1; // -1 to remove LF
int n = strlen(B) - 1;
for(int j = 0; j <= n; ++j)
{
for(int i = 0; i <= m; ++i)
{
score[0][i][j] = !i && !j ? 0 : std::min(
i > 0 ? score[0][i-1][j] + (A[i-1] == A[i]) : INT_MAX,
j > 0 ? score[1][i][j-1] + (B[j-1] == A[i]) : INT_MAX
);
score[1][i][j] = !i && !j ? 0 : std::min(
i > 0 ? score[0][i-1][j] + (A[i-1] == B[j]) : INT_MAX,
j > 0 ? score[1][i][j-1] + (B[j-1] == B[j]) : INT_MAX
);
}
}
printf("repetitions: %d\n", std::min(score[0][m-1][n], score[1][m][n-1]));
print_result(m, n);
printf("\n");
return 0;
}