傻瓜笔记之动态规划(二)——最长公共子串

示例字符串:

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         }

总结:没什么好说的,更最长子序列差不多一个模子,而且代码量显然比最长子序列少,因果过程就比它要简单的多。

原文地址:https://www.cnblogs.com/CHANGKTITI/p/11214290.html