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