[sdk/kommit] /: Fix LCS alghorithm

Hamed Masafi <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 1d8c7bfec16167cba4a8ec53cca90c1638366b8f by Hamed Masafi.
Committed on 18/08/2026 at 15:27.
Pushed by hamedmasafi into branch 'master'.

Fix LCS alghorithm

M  +1    -0    .gitignore
M  +15   -20   src/libkommitdiff/lcs.cpp
M  +31   -37   src/libkommitdiff/lcs.h

https://invent.kde.org/sdk/kommit/-/commit/1d8c7bfec16167cba4a8ec53cca90c1638366b8f

diff --git a/.gitignore b/.gitignore
index 69495346..f77ebdbf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,3 +105,4 @@ _packages/
 /.clang-format
 /compile_commands.json
 .cache
+.qtcreator/
diff --git a/src/libkommitdiff/lcs.cpp b/src/libkommitdiff/lcs.cpp
index 439a2b94..d4dde8cc 100644
--- a/src/libkommitdiff/lcs.cpp
+++ b/src/libkommitdiff/lcs.cpp
@@ -74,6 +74,7 @@ Solution longestCommonSubsequence(const QStringList &source, const QStringList &
 {
     Array2<int> l(source.size() + 1, target.size() + 1);
 
+    // 1. ساخت ماتریس LCS
     for (int i = 0; i <= source.count(); i++) {
         for (int j = 0; j <= target.count(); j++) {
             if (i == 0 || j == 0) {
@@ -86,36 +87,30 @@ Solution longestCommonSubsequence(const QStringList &source, const QStringList &
         }
     }
 
+    // 2. بازگشت به عقب (Backtracking) برای یافتن مسیر تطابق
     int i = source.count();
     int j = target.count();
     Solution r;
 
-    int si{-1};
-    int sj{-1};
-
     while (i > 0 && j > 0) {
         if (isEqual(source.at(i - 1), target.at(j - 1))) {
-            r.prepend(qMakePair(i - 1, j - 1));
-            i--;
-            j--;
-            si = i;
-            sj = j;
-        } else {
-            if (si != -1 && sj != -1) { }
-
-            int n = maxIn(l(i - 1, j), l(i, j - 1), l(i - 1, j - 1));
-            switch (n) {
-            case 1:
-                i--;
-                break;
-            case 2:
+            // نکته کلیدی اینجاست:
+            // اگر طول LCS با نادیده گرفتن عنصر فعلی target یکسان باشد، یعنی این عنصر
+            // جزو بهترین تطابق نیست. با انجام j--، الگوریتم مجبور می‌شود به عقب برگردد
+            // و اولین وقوع این خط را پیدا کند. این کار باعث گروه‌بندی صحیح خطوط اضافه
+            // شده در انتها می‌شود و از پراکندگی آن‌ها جلوگیری می‌کند.
+            if (l(i, j) == l(i, j - 1)) {
                 j--;
-                break;
-            default:
+            } else {
+                r.prepend(qMakePair(i - 1, j - 1));
                 i--;
                 j--;
-                break;
             }
+        } else if (l(i - 1, j) >= l(i, j - 1)) {
+            // ترجیح دادن i-- در حالت تساوی، یک استاندارد رایج برای تولید Diff پایدار است
+            i--;
+        } else {
+            j--;
         }
     }
 
diff --git a/src/libkommitdiff/lcs.h b/src/libkommitdiff/lcs.h
index 2e606eb4..7be8e998 100644
--- a/src/libkommitdiff/lcs.h
+++ b/src/libkommitdiff/lcs.h
@@ -53,6 +53,7 @@ template<typename T>
 {
     Array2<int> l(left.size() + 1, right.size() + 1);
 
+    // 1. ساخت ماتریس LCS
     for (int i = 0; i <= left.count(); i++) {
         for (int j = 0; j <= right.count(); j++) {
             if (i == 0 || j == 0) {
@@ -65,53 +66,46 @@ template<typename T>
         }
     }
 
+    // 2. بازگشت به عقب (Backtracking) برای یافتن Chunkهای تطابق
     int i = left.count();
     int j = right.count();
     QList<LcsResult> result;
 
     while (i > 0 && j > 0) {
         if (equals(left.at(i - 1), right.at(j - 1))) {
-            int leftEnd = i - 1;
-            int rightEnd = j - 1;
-            int leftStart = leftEnd;
-            int rightStart = rightEnd;
-
-            // Move diagonally while elements match
-            while (i > 0 && j > 0 && equals(left.at(i - 1), right.at(j - 1))) {
-                --i;
-                --j;
-                leftStart = i;
-                rightStart = j;
+            // نکته کلیدی اصلاح:
+            // اگر طول LCS با نادیده گرفتن عنصر فعلی از right (یا left) یکسان باشد،
+            // یعنی این عنصر جزو "ضروری‌ترین" تطابق‌ها نیست. با عقب گرد (j-- یا i--)،
+            // الگوریتم مجبور می‌شود به عقب برگردد و اولین وقوع ممکن را پیدا کند.
+            // این کار باعث می‌شود آکولادهای پایانی به درستی به بلوک اصلی خود گره بخورند
+            // و بلوک‌های جدید به صورت یکپارچه در انتها به عنوان Insert شناسایی شوند.
+            if (l(i, j) == l(i, j - 1)) {
+                j--;
+            } else if (l(i, j) == l(i - 1, j)) {
+                i--;
+            } else {
+                // این یک تطابق ضروری است. حالا تمام خطوط متوالی یکسان را پیدا کن (Chunk)
+                int leftEnd = i - 1;
+                int rightEnd = j - 1;
+                int leftStart = leftEnd;
+                int rightStart = rightEnd;
+
+                while (i > 0 && j > 0 && equals(left.at(i - 1), right.at(j - 1))) {
+                    --i;
+                    --j;
+                    leftStart = i;
+                    rightStart = j;
+                }
+
+                result.prepend({leftStart, leftEnd, rightStart, rightEnd});
             }
-
-            // Add the matched subsequence to result
-            result.prepend({leftStart, leftEnd, rightStart, rightEnd});
-
-            // i--;
-            // j--;
-            // if (!started) {
-            //     si = i;
-            //     sj = j;
-            //     started = true;
-            // }
         } else {
-            // if (started) {
-            //     r << LcsResult{si, i, sj, j};
-            //     started = false;
-            // }
-
-            int n = maxIn(l(i - 1, j), l(i, j - 1), l(i - 1, j - 1));
-            switch (n) {
-            case 1:
-                i--;
-                break;
-            case 2:
-                j--;
-                break;
-            default:
+            // منطق استاندارد و صحیح بازگشت به عقب در LCS
+            // در حالت تساوی، ترجیح با i-- است که منجر به Diff پایدارتر (Stable) می‌شود
+            if (l(i - 1, j) >= l(i, j - 1)) {
                 i--;
+            } else {
                 j--;
-                break;
             }
         }
     }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.