求兩個字符串的不連續的公共字串

不管題目怎麼樣,始終有一個宗旨,那就是一步一步接近結果,最後你會發現一個最終最優化的解題思路,若是你直接看最優解題思路,會比較突兀,理解起來不是很好,一步一步想,會感受水到渠成,最後你會發現任何已有的算法都基於一個很樸素的想法。ios

問題描述算法

給定兩個字符串,求出字符串中最長匹配的公共字符串,並且能夠不用相連,好比"abcdef"和"abfce",咱們能夠找到「abce」是最長的公共字符串數組

問題分解優化

其實上述的問題,能夠分解成兩個子問題spa

(1)首先,找出相連的公共子字符串,好比"abcdef"和"abfce",結果就是"ab"code

(2)其次,再考慮這個問題,即不相連的公共字符串blog

對第一個子問題的分析字符串

 一拿到這個問題,最暴力的方法就是窮舉,再一一比較字符串。string

再想一想,咱們就能夠發現字符串的比較,其實就是比較字符。爲了不重複的比較,咱們用一個二維數組記錄以前的比較結果。io

下面以"abfcd", "abcdef"爲例,加以說明。

[ab, ab]而言,對於的是2,也就是說最長公共字串是2.

[abf, ab]而言,在3*2的矩陣中,最大的數字也是2,那就意味着最長公共長度也是2.

比較字符是否相同,若兩字符相同,則對於的數字是斜對角的數字加一。

至於結果的打印,咱們能夠經過記錄下最大的長度,再找出全部最大的長度,就能夠打印全部的結果了。

這樣就把窮舉簡化成時間複雜度爲o(n*m)的問題,其實這就是動態規劃,其本質就是用空間記錄以前的比較結果,以此來達到優化時間複雜度的目的。

該算法空間負責度爲o(n*m),咱們還能夠進行再一步優化爲o(n)的時間複雜度,其思路跟揹包問題類似,至因而逆序,順序均可以,可是爲了打印全部結果的話,必須得用逆序實現。

 1 //find the successive common substring
 2 #include <stdio.h>
 3 #include <iostream>
 4 using namespace std;
 5 #define N 100
 6 int a[N + 5][N + 5];
 7 char s1[N + 5] = "abfcd";
 8 char s2[N + 5] = "abcdef";
 9 void main()
10 {
11     int i, j, k, len1, len2, max, count;
12     //---------------o(n * n)
13     max = 0;
14     for (i = 0; s1[i] != '\0'; i++)
15     {
16         for (j = 0; s2[j] != '\0'; j++)
17         {
18             if (s1[i] == s2[j])
19             {
20                 a[i + 1][j + 1] = a[i][j] + 1;
21                 if (max < a[i + 1][j + 1])
22                     max = a[i + 1][j + 1];
23             }
24             else
25             {
26                 a[i + 1][j + 1] = 0;
27             }
28         }
29     }
30     //--------for check
31     /**/
32     for (i = 0; s1[i] != '\0'; i++)
33     {
34         for (j = 0; s2[j] != '\0'; j++)
35         {
36             printf("%d ", a[i + 1][j + 1]);
37         }
38         puts("");
39     }
40     
41     //----------print the result
42     count = 0;
43     for (i = 0; s1[i] != '\0'; i++)
44     {
45         for (j = 0; s2[j] != '\0'; j++)
46         {
47             if (a[i + 1][j + 1] == max)
48             {
49                 for (k = i - max + 1; k <= i; k++)
50                     printf("%c", s1[k]);
51                 count++;
52                 puts("");
53             }
54             
55         }
56     }
57     printf("total : %d\n", count);
58     system("pause");
59 }

 對第二個子問題的分析

瞭解了第一問的解題思路,稍加修改,就能夠解決第二問的問題。

咱們第一問是若相同則斜對角加一,若不一樣,則置爲0,這裏是若相同,則斜對角加一,若不一樣則取反斜對角中較大的數。

咱們能夠這麼理解,f(n, m)表示的是長度爲n和長度爲m的字符串的公共字串的長度,則if str1[n] == str2[m], f(n, m) = f(n - 1, m - 1) + 1; if str1[n] != str2[m], f(n, m) = max(f(n - 1, m), f(n, m - 1))

好了,那接下來的問題就是如何打印的問題,僅僅是以上的數字,咱們發現要想打印還得花一些功夫。因此,咱們在上面的運算過程當中,還記錄了另外一個數據,就是方向,就是說當前這個值時從斜對角來的,dir就是1;如果從左邊來的,dir就是2;如果從上面來的,dir就是3。有了這個以後,打印就垂手可得了。

 

 1 #include <stdio.h>
 2 #include <iostream>
 3 using namespace std;
 4 #define N 100
 5 struct
 6 {
 7     int len;
 8     int dir;
 9 }a[N + 5][N + 5];
10 char s1[N + 5] = "cdbef";
11 char s2[N + 5] = "abcdef";
12 char res[N + 5];
13 void main()
14 {
15     int i, j, k, len1, len2, count;    
16     //---------------o(n * n)
17     for (i = 0; s1[i] != '\0'; i++)
18     {
19         for (j = 0; s2[j] != '\0'; j++)
20         {
21             if (s1[i] == s2[j])
22             {
23                 a[i + 1][j + 1].len = a[i][j].len + 1;
24                 a[i + 1][j + 1].dir = 1;
25             }
26             else
27             {
28                 if (a[i][j + 1].len > a[i + 1][j].len)
29                 {
30                     a[i + 1][j + 1].len = a[i][j + 1].len;
31                     a[i + 1][j + 1].dir = 3;
32                 }
33                 else
34                 {
35                     a[i + 1][j + 1].len = a[i + 1][j].len;
36                     a[i + 1][j + 1].dir = 2;
37                 }
38             }
39         }
40     }
41     //--------for check
42     /**/
43     for (i = 0; s1[i] != '\0'; i++)
44     {
45         for (j = 0; s2[j] != '\0'; j++)
46         {
47             printf("%d ", a[i + 1][j + 1].len);
48         }
49         puts("");
50     }
51     puts("");
52     for (i = 0; s1[i] != '\0'; i++)
53     {
54         for (j = 0; s2[j] != '\0'; j++)
55         {
56             printf("%d ", a[i + 1][j + 1].dir);
57         }
58         puts("");
59     }
60     
61     //----------print the result
62     len1 = i;
63     len2 = j;
64     int len = a[len1][len2].len;
65     for (k = len - 1; k >= 0;)
66     {
67         if (a[i][j].dir == 1)
68         {
69             res[k] = s1[i - 1];
70             k--;
71             i--;
72             j--;
73         }
74         else if (a[i][j].dir == 2)
75         {
76             j--;
77         }
78         else if (a[i][j].dir == 3)
79         {
80             i--;
81         }
82     }
83 
84     for (k = 0; k < len; k++)
85     {
86         printf("%c", res[k]);
87     }
88     puts("");
89     system("pause");
90 }
相關文章
相關標籤/搜索