簡體   English   中英

最長的共同子序列:這為什么錯?

[英]longest common subsequence: why is this wrong?

int lcs(char * A, char * B)
{
  int m = strlen(A);
  int n = strlen(B);
  int *X = malloc(m * sizeof(int));
  int *Y = malloc(n * sizeof(int));
  int i;
  int j;
  for (i = m; i >= 0; i--)
    {
      for (j = n; j >= 0; j--)
        {
          if (A[i] == '\0' || B[j] == '\0') 
              X[j] = 0;
          else if (A[i] == B[j]) 
              X[j] = 1 + Y[j+1];
          else 
              X[j] = max(Y[j], X[j+1]);
        }
      Y = X;
    }
  return X[0];
}

這有效,但是valgrind大聲抱怨無效讀取。 我是怎么弄亂記憶的? 抱歉,我總是在C內存分配上失敗。

這里的問題是表的大小。 請注意,您正在將空間分配為

int *X = malloc(m * sizeof(int));
int *Y = malloc(n * sizeof(int));

但是,您使用的索引為0 ... m和0 ... n,這意味着X中需要m + 1個插槽,Y中需要n + 1個插槽。

嘗試將其更改為閱讀

int *X = malloc((m + 1) * sizeof(int));
int *Y = malloc((n + 1) * sizeof(int));

希望這可以幫助!

系列問題。 首先,正如templatetypedef所說,您的分配不足。

然后,就像paddy所說的那樣,您並沒有釋放malloc分配的內存。 如果需要Y=X行,則需要將原始的malloc分配的空間地址存儲在另一組變量中,以便可以對其進行free調用。

...mallocs...
int * original_y = Y;
int * original_x = X;
...body of code...
free(original_y);
free(original_x);
return X[0];

但這不能解決您的新問題,這就是為什么代碼實際上不起作用?

我承認我無法遵循您的代碼(沒有做更多的研究),但是我可以提出一種行之有效且容易理解的算法。 這可能是偽代碼 ,並不是特別有效,但是第一步是使其正確。 我稍后列出了一些優化。

int lcs(char * A, char * B)
{
  int length_a = strlen(A);
  int length_b = strlen(B);


  // these hold the position in A of the longest common substring
  int longest_found_length = 0;

  // go through each substring of one of the strings (doesn't matter which, you could pick the shorter one if you want)
  char * candidate_substring = malloc(sizeof(char) * length_a + 1);
  for (int start_position = 0; start_position < length_a; start_position++) {
    for (int end_position = start_position; end_position < length_a; end_position++) {

       int substring_length = end_position - start_position + 1;

       // make a null-terminated copy of the substring to look for in the other string
       strncpy(candidate_substring, &(A[start_position]), substring_length);
       if (strstr(B, candidate_substring) != NULL) {
         longest_found_length = substring_length;
       }
    }

  }
  free(candidate_substring);
  return longest_found_length;
}

您可以執行一些不同的優化:

       // if this can't be longer, then don't bother checking it.  You can play games with the for loop to not have this happen, but it's more complicated.
       if (substring_length <= longest_found_index) {
         continue;
       }

       // there are more optimizations you could do to this, but don't check
       //   the substring if it's longer than b, since b can't contain it.
       if (substring_length > length_b) {
         continue;
       } 

   if (strstr(B, candidate_substring) != NULL) {
     longest_found_length = end_position - start_position + 1;
   } else {
     // if nothing contains the shorter string, then nothing can contain the longer one, so skip checking longer strings with the same starting character
     break; // skip out of inner loop to next iteration of start_position
   }

不必將每個候選子字符串復制到新字符串,而是可以使用end_position + 1NUL字符進行字符交換。 然后,在b中查找該子字符串后,將end_position+1處的原始字符end_position+1 。這會快得多,但會使實現復雜一些。

注意:我通常不會寫兩個答案,如果您覺得它很俗氣,請隨時對此一句話發表評論並注意投票。 這個答案是一個更優化的解決方案,但是我想給出一個我能想到的最簡單的答案,然后將其放在另一個答案中,以免混淆兩者。 基本上,它們是針對不同的受眾的。

有效解決此問題的關鍵是在尋找較長的子字符串時,不要丟棄有關較短的公共子字符串的信息。 天真地,您將每個子字符串與另一個子字符串進行檢查,但是如果您知道“ AB”在“ ABC”中匹配,並且您的下一個字符是C,則無需檢查“ ABC”是否在“ ABC”中,只需檢查一下即可“ AB”后面的點是“ C”。

對於A中的每個字符,您必須最多檢查B中的所有字母,但是由於一旦不再有更長的子字符串,我們將停止瀏覽B,因此大大限制了檢查次數。 每當您在前面獲得更長的匹配項時,就無需在后端進行檢查,因為它不再是更長的子字符串。

例如,如果A和B都很長,但不包含公共字母,則在A * B運行時,將A中的每個字母與B中的每個字母進行比較。

對於有很多匹配項但匹配長度不是較短字符串長度很大一部分的序列,您可以使用A * B組合來檢查兩個字符串(A或B)中較短的字符串到A * B * A或A * B * B,對於相似長度的字符串,這基本上是O(n ^ 3)時間。 我確實認為,即使存在三重嵌套的for循環,此解決方案中的優化也會比n ^ 3更好,但它似乎並不如我所知。

不過,我還在考慮這一點。 要么找到的子字符串都不占字符串長度的很大一部分,在這種情況下,優化不會做很多事情,但是對A * B的每個組合的比較都不會與A或B進行比例縮放,從而得出是常數-或-它們是A和B的重要部分,並且直接除以必須比較的A * B組合。

我可能會問一個問題。

int lcs(char * A, char * B)
{
  int length_a = strlen(A);
  int length_b = strlen(B);

  // these hold the position in A of the longest common substring
  int longest_length_found = 0;

  // for each character in one string (doesn't matter which), look for incrementally larger strings in the other
  for (int a_index = 0; a_index < length_a - longest_length_found; a_index++) {
    for (int b_index = 0; b_index < length_b - longest_length_found; b_index++) {

       // offset into each string until end of string or non-matching character is found
      for (int offset = 0; A[a_index+offset] != '\0' && B[b_index+offset] != '\0' && A[a_index+offset] == B[b_index+offset]; offset++) {          
        longest_length_found = longest_length_found > offset ? longest_length_found : offset;
      }
    }
  }
  return longest_found_length;
}

除了templatetypedef所說的以外,還需要考慮以下幾點:

  • 為什么XY的大小不一樣?
  • 為什么要做Y = X 那是指針的分配。 你可能是說memcpy(Y, X, (n+1)*sizeof(int))嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM