【问题标题】:What is the minimum space complexity in problem of find longest common substring?查找最长公共子串问题的最小空间复杂度是多少?
【发布时间】:2021-05-21 21:49:43
【问题描述】:
const char *_longest_common_substr(const char *s1, const char *s2, int n, int m) {
   // assert n >= m
   int max = 0; // keep track of max length found
   int tmp = 0; // value is incremented till letters match
   int begin = 0;
   int try_begin = 0; // possible new begin index
   int end = 0; // rv = s2[begin : end - 1]
   
   for (int i = 0; i < n; i++) {
       tmp = 0;
       try_begin = 0;
       // s1 is scanned circularly
       for (int j = 0; j < m; j++) {
           int index = (j + i) % n; // current s1 index
           if (index < n && s1[index] == s2[j]) {
               if (tmp == 0)
                   try_begin = j;
               tmp++;
           } else {
               tmp = 0;
           }
           if (tmp > max) {
               max = tmp;
               begin = try_begin;
               end = j + 1;
           }
       }
   }
       
   int size = begin >= end ? 0 : end - begin;
   char *rv = malloc((size + 1) * sizeof(*rv));
   int i;
   for (i = 0; i < size; i++)
       rv[i] = s2[begin + i];
   rv[i] = '\0';
   return rv;
}

const char *longest_common_substr(const char *s1, const char *s2, int n, int m) {
   if (n < m)
       return _longest_common_substr(s2, s1, m, n);
   return _longest_common_substr(s1, s2, n, m);
}

此代码是否正确找到最长的公共子字符串?我不明白为什么在很多地方,例如wikipedia,他们使用矩阵来解决问题,而在这个看似简单的解决方案中不需要它并且时间复杂度仍然是 O(n*m ),而空间复杂度为 O(1)。 一个可能的测试是

int main() {
    const char *str1 = "identification";
    const char *str2 = "administration";
    
    printf("%s\n", longest_common_substr(str1, str2, strlen(str1), strlen(str2)));
    
}

输出是

ation

子字符串是循环的,所以输入

action
tionac

输出将是

tionac

无论如何,我可以在字符串末尾添加两个不同的无效字符来删除此属性

【问题讨论】:

  • 这段代码是否正确。您应该尝试自己通过针对代码运行测试用例来回答这个问题。
  • 是的,我做到了并且有效
  • 您需要更好的变量名称。你的算法很难理解
  • @bolov 我添加了一些 cmets
  • _longestCommonSubstr 名称保留给 C++ 中全局命名空间中的语言实现。通过定义它,程序的行为将是未定义的。您应该为该函数使用另一个名称。

标签: c algorithm time-complexity


【解决方案1】:

您的算法执行循环匹配,这与经典 LCS 算法不同。然而,它可以简化为删除这个额外的匹配,也不需要使 s1 更长的字符串:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *longest_common_substr(const char *s1, const char *s2, int n1, int n2) {
    int begin = 0;  // offset of lcs in s2
    int max = 0;    // length of lcs

    for (int i = 0; i < n1; i++) {
        int tmp = 0;        // count of matched letters
        int index = i;      // current s1 index
        for (int j = 0; j < n2; j++, index++) {
            if (index == n1) {
                index = 0;
                tmp = 0;    // do not match circularly
            }
            if (s1[index] == s2[j]) {
                tmp++;
                if (tmp > max) {
                    max = tmp;
                    begin = j - (tmp - 1);
                }
            } else {
                tmp = 0;
            }
        }
    }

    char *rv = malloc(sizeof(*rv) * (max + 1));
    memcpy(rv, s2 + begin, max);
    rv[max] = '\0';
    return rv;
}

int main(int argc, char *argv[]) {
    if (argc == 3) {
        const char *s1 = argv[1];
        const char *s2 = argv[2];
        char *p = longest_common_substr(s1, s2, strlen(s1), strlen(s2));
        if (p) {
            printf("lcs('%s','%s') -> '%s'\n", s1, s2, p);
        }
    }
    return 0;
}

上面确实有O(n1*n2)的时间复杂度和O(1)的空间复杂度。实现更好的时间复杂度通常是一个理想的目标,这可以解释为什么 Wikipedia 文章对于更复杂的情况有一个次优示例。

【讨论】:

    【解决方案2】:

    你的算法很奇怪。您只计算最长字符串上的循环子字符串。如果字符串长度相等,则仅计算第一个上的圆形子字符串。

    例如:

    • 对于"bxa""ab",您会找到解决方案"ab",因为您在bxa 上计算循环子字符串

    • 但对于 "bxa""zaby",您不会考虑在 "bxa" 中使用循环子字符串,并且您找不到 "ab" 作为解决方案

    • ("abx", "bya")("bya", "abx") 有不同的解决方案,即使只有参数的位置发生了变化。 (如果两个参数长度相等,则仅在第一个参数上计算循环子字符串)。

    您必须:

    • 根本不计算循环子串(维基链接中描述的经典算法)或:

    • 计算两个字符串的循环子字符串(这是一个不同的算法然后最长公共子字符串

    因此,您的算法的复杂性无法与 wiki 算法相比,因为它是解决不同问题的不同算法。

    【讨论】:

    • 我可以在每个字符串的末尾添加一个不同的无效字符来重置计数,所以问题是一样的,或者我可以利用 '\0' 来重置计数
    猜你喜欢
    • 2013-05-28
    • 2018-11-24
    • 2022-01-19
    • 2018-09-09
    • 1970-01-01
    • 2014-04-16
    • 2023-03-24
    • 2012-10-07
    • 2016-04-13
    相关资源
    最近更新 更多