imashutosh51

Print Shortest Common Supersequence

Mar 6th, 2026 (edited)
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.41 KB | None | 0 0
  1. '''
  2. LCS Variation — Shortest Common Supersequence (SCS)
  3.  
  4. WHAT IS SCS?
  5. ------------
  6. A string that contains both s1 and s2 as subsequences.
  7.  
  8.    s1 = "abac"
  9.    s2 = "cab"
  10.    SCS = "cabac"  → you can extract both s1 and s2 from this as subsequences.
  11.  
  12. WHY USE LCS?
  13. ------------
  14. SCS length = len(s1) + len(s2) - LCS length
  15.  
  16. The idea: characters that are common to both strings (i.e., part of LCS)
  17. don't need to be written twice — once serves both strings.
  18. Every non-LCS character is unique to one string, so it must appear separately.
  19.  
  20.    s1="abac", s2="cab", LCS="ab" (length 2)
  21.    SCS length = 4 + 3 - 2 = 5  →  "cabac" ✓
  22.  
  23. STEP 1 — BUILD THE DP TABLE:
  24. -----------------------------
  25. dp[i][j] = LCS length between s1[0..i] and s2[0..j]
  26.  
  27. Run the standard LCS memoization. After this, the dp table is fully filled
  28. and ready to be traced back.
  29.  
  30. STEP 2 — TRACEBACK TO BUILD THE SCS STRING:
  31. --------------------------------------------
  32. Start from i = len(s1)-1, j = len(s2)-1 and walk backwards.
  33. At each step, ask: "which character belongs here in the SCS?"
  34.  
  35. There are two cases:
  36.  
  37. CASE 1 — Characters match (s1[i] == s2[j]):
  38.    This character is part of the LCS.
  39.    LCS characters are written ONCE — they satisfy both strings simultaneously.
  40.    → Add s1[i] (or s2[j], same thing) to answer, move diagonally: i--, j--
  41.  
  42. CASE 2 — Characters don't match:
  43.    Figure out which character was "ignored" (skipped) to build the LCS.
  44.  
  45.        up   = dp[i-1][j]   # LCS value if we ignore s1[i]
  46.        left = dp[i][j-1]   # LCS value if we ignore s2[j]
  47.  
  48.    The LCS came from the LARGER of these two directions.
  49.  
  50.    if up > left:
  51.        → LCS was built by ignoring s1[i]
  52.        → So s1[i] is NOT in the LCS — it's unique to s1
  53.        → We must include it in SCS now, then move i--
  54.  
  55.    else (left >= up):
  56.        → LCS was built by ignoring s2[j]
  57.        → So s2[j] is NOT in the LCS — it's unique to s2
  58.        → We must include it in SCS now, then move j--
  59.  
  60.    KEY INSIGHT: In LCS traceback, we moved toward the LARGER side to find
  61.    common characters. Here we do the SAME — but instead of printing the
  62.    common character, we print the character that got IGNORED (the one
  63.    belonging to the string we're moving away from). That ignored character
  64.    is non-LCS, so it must appear in SCS on its own.
  65.  
  66. STEP 3 — DRAIN REMAINING CHARACTERS:
  67. --------------------------------------
  68. When one pointer goes out of bounds, the other string still has characters left.
  69. ALL of them must be included — skipping any character would break the
  70. subsequence property for that string.
  71.  
  72.    while i >= 0: add s1[i], i--
  73.    while j >= 0: add s2[j], j--
  74.  
  75. STEP 4 — REVERSE:
  76. -----------------
  77. Since we built the answer backwards (from end to start), reverse it at the end.
  78.  
  79. IN ONE LINE:
  80. ------------
  81. LCS characters are written ONCE (shared by both strings).
  82. Non-LCS characters are written SEPARATELY (each string owns its own).
  83. The traceback simply figures out, at each step, which string "owns"
  84. the current character — and the dp values tell you exactly that.
  85. '''
  86.  
  87. def fun(s1,s2,n,m,dp):
  88.     if n<0 or m<0:
  89.         return 0
  90.     if dp[n][m]!=-1:
  91.         return dp[n][m]
  92.     if s1[n]==s2[m]:
  93.         dp[n][m]=1+fun(s1,s2,n-1,m-1,dp)
  94.     else:
  95.         dp[n][m]=max(fun(s1,s2,n-1,m,dp) , fun(s1,s2,n,m-1,dp))
  96.     return dp[n][m]
  97.  
  98. class Solution:
  99.     def shortestCommonSupersequence(self, str1: str, str2: str) -> str:
  100.         dp=[[-1]*len(str2) for _ in range(len(str1))]
  101.         fun(str1,str2,len(str1)-1,len(str2)-1,dp)
  102.  
  103.         i=len(str1)-1
  104.         j=len(str2)-1
  105.         ans=''
  106.         while i>=0 and j>=0:
  107.             if str1[i]==str2[j]:
  108.                 ans+=str1[i]
  109.                 i-=1
  110.                 j-=1
  111.             else:
  112.                 up=dp[i-1][j] if i-1>=0 else 0 #here up is ignoring the ith character
  113.                 left=dp[i][j-1] if j-1>=0 else 0 #here left is ignoring the jth character
  114.                
  115.                 if up>left: #it means,subsequence is coming from the top means LCS ignored the ith char so include str1[i] in scs seprately
  116.                     ans+=str1[i]
  117.                     i-=1
  118.                 else:
  119.                     ans+=str2[j]
  120.                     j-=1
  121.  
  122.         while i>=0:
  123.             ans+=str1[i]
  124.             i-=1
  125.         while j>=0:
  126.             ans+=str2[j]
  127.             j-=1
  128.  
  129.         return ''.join(reversed(ans))
  130.  
Advertisement
Add Comment
Please, Sign In to add comment