最长公共子序列递归备忘录,动态规划算法实验报告

本文通过实验比较了递归备忘录和动态规划算法在计算最长公共子序列时的时间效率,结果显示随着字符串长度的增长,动态规划的时间效率优于递归备忘录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验内容:

(1)最长公共子序列递归备忘录,动态规划算法比较。

(2)画出程序运行时间与n*m的关系图,解释实验结果。

递归代码:

// 递归备忘录方法求解最长公共子序列长度
int memoizationLCS(char* x, char* y, int i, int j, int** memo) {
    if (i == 0 || j == 0) {
        return 0;
    }
    if (memo[i][j] != -1) {
        return memo[i][j];
    }
    if (x[i - 1] == y[j - 1]) {
        memo[i][j] = memoizationLCS(x, y, i - 1, j - 1, memo) + 1;
    }
    else {
        memo[i][j] = max(memoizationLCS(x, y, i, j - 1, memo), memoizationLCS(x, y, i - 1, j, memo));
    }
    return memo[i][j];
}

动态规划代码:

// 动态规划方法求解最长公共子序列长度
int dynamicLCS(char* x, char* y, int m, int n) {
    int** L = (int**)malloc((m + 1) * sizeof(int*));
    for (int i = 0; i <= m; i++) {
        L[i] = (int*)malloc((n + 1) * sizeof(int));
    }

    // 初始化L数组
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if (i == 0 || j == 0) {
                L[i][j] = 0;
            }
            else if (x[i - 1] == y[j - 1]) {
                L[i][j] = L[i - 1][j - 1] + 1;
            }
            else {
                L[i][j] = max(L[i - 1][j], L[i][j - 1]);
            }
        }
    }

递归和动态规划运行结果:

最长公共子序列递归备忘录和动态规划n*m(0<n*m<200000)个数时间效率对比:

实验结果:

由运行结果可视化分析可以得出,当n逐渐增大后,递归备忘录所花费的时间高于动态规划的时间。

原因分析:

1)递归备忘录:

随着字符串长度的增加,程序的运行时间呈指数级增长。这是由于递归备忘录方法的时间复杂度是O(n*m),需要存储大量中间计算结果来减少重复计算,从而提高效率。

2)动态规划:

随着字符串长度的增加,程序的运行时间呈线性增长。这是由于动态规划方法通过填表法计算最长公共子序列,不需要进行递归调用和重复计算,时间复杂度为O(n*m)。

源码:

//最长公共子序列递归备忘录,动态规划算法比较
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int max(int a, int b) {
    return (a > b) ? a : b;
}

// 递归备忘录方法求解最长公共子序列长度
int memoizationLCS(char* x, char* y, int i, int j, int** memo) {
    if (i == 0 || j == 0) {
        return 0;
    }
    if (memo[i][j] != -1) {
        return memo[i][j];
    }
    if (x[i - 1] == y[j - 1]) {
        memo[i][j] = memoizationLCS(x, y, i - 1, j - 1, memo) + 1;
    }
    else {
        memo[i][j] = max(memoizationLCS(x, y, i, j - 1, memo), memoizationLCS(x, y, i - 1, j, memo));
    }
    return memo[i][j];
}

// 动态规划方法求解最长公共子序列长度
int dynamicLCS(char* x, char* y, int m, int n) {
    int** L = (int**)malloc((m + 1) * sizeof(int*));
    for (int i = 0; i <= m; i++) {
        L[i] = (int*)malloc((n + 1) * sizeof(int));
    }

    // 初始化L数组
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if (i == 0 || j == 0) {
                L[i][j] = 0;
            }
            else if (x[i - 1] == y[j - 1]) {
                L[i][j] = L[i - 1][j - 1] + 1;
            }
            else {
                L[i][j] = max(L[i - 1][j], L[i][j - 1]);
            }
        }
    }

    int lcsLength = L[m][n];

    // 释放L数组的内存
    for (int i = 0; i <= m; i++) {
        free(L[i]);
    }
    free(L);

    return lcsLength;
}

// 生成随机字符串
void RandomString(char* str, int length) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int charsetSize = sizeof(charset) - 1;

    srand(time(NULL));

    for (int i = 0; i < length; i++) {
        int index = rand() % charsetSize;
        str[i] = charset[index];
    }

    str[length] = '\0';
}

int main(int argc, char* argv[]) {
    int n = 1220;
    char* x = (char*)malloc((n + 1) * sizeof(char));
    RandomString(x, n);
    int m = 1550;
    char* y = (char*)malloc((m + 1) * sizeof(char));
    RandomString(y, m);

    // 使用递归备忘录方法求解最长公共子序列长度
    int** memo = (int**)malloc((n + 1) * sizeof(int*));
    for (int i = 0; i <= n; i++) {
        memo[i] = (int*)malloc((m + 1) * sizeof(int));
        memset(memo[i], -1, (m + 1) * sizeof(int));
    }
    clock_t start = clock();
    int lcsLengthMemoization = memoizationLCS(x, y, n, m, memo);
    clock_t end = clock();
    printf("递归备忘录方法求解最长公共子序列长度: %d\n", lcsLengthMemoization);
    printf("递归备忘录方法运行时间: %f seconds\n", (double)(end - start) / CLOCKS_PER_SEC);

    // 释放memo数组的内存
    for (int i = 0; i <= n; i++) {
        free(memo[i]);
    }
    free(memo);

    // 使用动态规划方法求解最长公共子序列长度
    start = clock();
    int lcsLengthDynamic = dynamicLCS(x, y, n, m);
    end = clock();
    printf("动态规划方法求解最长公共子序列长度: %d\n", lcsLengthDynamic);
    printf("动态规划方法运行时间: %f seconds\n", (double)(end - start) / CLOCKS_PER_SEC);

    // 释放输入串x和y的内存
    free(x);
    free(y);

    // 输出n和m的乘积与程序运行时间之间的关系
    double timeMemoization = (double)(end - start) / CLOCKS_PER_SEC;
    start = clock();
    for (int i = 0; i < n * m; i++) {
        // do nothing
    }
    end = clock();
    double timeDummy = (double)(end - start) / CLOCKS_PER_SEC;
    printf("程序运行时间: %f seconds\n",timeMemoization - timeDummy);

    printf("\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值