示例字符串:
X = "abcopms" Y = "opmkabc" , 最长公共子串Z = "abc opm"
状态转移思路:由于记录的是最长的连续的子串,那么只需要在碰到相同的字符的时候做标记即可,不需要像最长子序列那样做状态转移的记录,即该问题的状态转换方程如下:
Z = 1, z[i,j] = 0 i==0 or j == 0
2, z[i,j] = z[i-1,j-1]+1 x[i] == y[j] 解1
3, z[i,j] = 0 x[i]!=y[j]
解1:匹配到相同的字符的时候,就在两个字符串前面的基数上面增加1,意味着最长子串长度加一(到遍历的目前为止),这个是该方程唯一一个状态转移的过程。
下面贴上全文代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 最长子串 8 { 9 class Program 10 { 11 public static string s1 = "abcopms"; 12 public static string s2 = "opmkabc"; 13 public static int maxLength = 0; // 记录最长的长度 14 public static int [,] c = new int[s1.Length+1, s2.Length+1]; 15 static void Main(string[] args) 16 { 17 MaxLength(); 18 for (int i = 0; i < s1.Length + 1; ++i) 19 { 20 for (int j = 0; j < s2.Length + 1; ++j) 21 { 22 Console.Write(c[i, j]); 23 } 24 Console.Write(" "); 25 } 26 Console.WriteLine("这是所有子串"); 27 for(int i = 0;i<s1.Length+1;++i) 28 { 29 for(int j = 0;j<s2.Length+1;++j) 30 { 31 if (c[i, j] == maxLength) 32 { 33 Translate(i, j); 34 Console.Write(" "); 35 } 36 } 37 } 38 Console.ReadKey(); 39 } 40 41 public static void Translate(int x,int y) 42 { 43 if (c[x, y] == 0) return; 44 else 45 { 46 Translate(x - 1, y - 1); 47 //Console.WriteLine("printf"); 48 Console.Write(s1[x - 1]); 49 } 50 } 51 52 public static int [,] MaxLength() 53 { 54 Console.WriteLine("这是状态矩阵"); 55 Console.WriteLine(s1); 56 Console.WriteLine(s2); 57 for(int i = 0;i<s1.Length+1;++i) 58 { 59 for(int j=0;j<s2.Length+1;++j) 60 { 61 if (i == 0 || j == 0) 62 c[i, j] = 0; 63 else if (s1[i-1] == s2[j-1]) 64 { 65 c[i, j] = c[i - 1, j - 1] + 1; 66 maxLength = Math.Max(maxLength, c[i, j]); 67 } 68 else 69 { 70 c[i, j] = 0; 71 } 72 } 73 } 74 return c; 75 } 76 } 77 }
完成状态转移的代码片段如下:
1 public static int [,] MaxLength() 2 { 3 Console.WriteLine("这是状态矩阵"); 4 Console.WriteLine(s1); 5 Console.WriteLine(s2); 6 for(int i = 0;i<s1.Length+1;++i) 7 { 8 for(int j=0;j<s2.Length+1;++j) 9 { 10 if (i == 0 || j == 0) 11 c[i, j] = 0; 12 else if (s1[i-1] == s2[j-1]) 13 { 14 c[i, j] = c[i - 1, j - 1] + 1; 15 maxLength = Math.Max(maxLength, c[i, j]); 16 } 17 else 18 { 19 c[i, j] = 0; 20 } 21 } 22 } 23 return c; 24 }
该片段执行完毕之后返回一个二维数组,记录此次转移的过程
完成状态转移数组遍历的代码如下:
1 for(int i = 0;i<s1.Length+1;++i) 2 { 3 for(int j = 0;j<s2.Length+1;++j) 4 { 5 if (c[i, j] == maxLength) 6 { 7 Translate(i, j); 8 Console.Write(" "); 9 } 10 } 11 }
遍历转移数组的过程中,只有碰到最大值得时候才是最长子串的时候,但是往往最长的子串又不止一个,如果题目规定只有一个最长子串的话,那么只要记录在记录maxLength时同步记录下i,j,即可直接进行输出而不必遍历整个数组。
完成子串输出的代码如下:
1 public static void Translate(int x,int y) 2 { 3 if (c[x, y] == 0) return; 4 else 5 { 6 Translate(x - 1, y - 1); 7 Console.Write(s1[x - 1]); 8 } 9 }
总结:没什么好说的,更最长子序列差不多一个模子,而且代码量显然比最长子序列少,因果过程就比它要简单的多。