最长公共子字符串
關于題目理解,請注意和最長公共子序列的區別,最長公共子字符串的解法是動態規劃,但是比較難想到表的構造方法。
注意到,設給定字符串為str1 和 str2 ,二者的長度分別是 len1 和 len2 ,那么解空間大小之多是len1*len2?(假設最長公共子字符串為substr_common,那么substr_common在str1 中的結束位置或者起始位置只有len1種選擇,而在str2中則最多len2種選擇,故解空間最大為len1*len2)。對于解空間中的每一個解都對應著自己的長度,因為一旦結束位置都確定,即意味著公共子字符串找到了。
如果用蠻力法做的話,遍歷解空間需要時間復雜度為O(len1*len2),然后確定每個解的長度需要的時間是O(min(len1,len2)),所以基本上是個O(n3)的算法,顯然不妥。
?
前面說過,這道題可以用動態規劃來做,動態規劃最關鍵的是找到最優子結構,一般來說,最優子結構意味著問題可以找到一種遞歸的,不斷縮小問題規模的解決方式,與分治法不同,動態規劃的小問題之間有重疊,但是這個問題好像不是那么好找到一種遞歸的表達形式。
要縮小問題規模,一種最簡單的想法肯定是縮小str1規模,或者縮小str2規模,或者二者同時縮小,而縮小的方式肯定是增減元素了,能用動態規劃做必定有個自底向上的過程,很少有直接二分的, 直接二分是分治法的策略,所以增減一般在頭尾增減,以此題為例,為保持一致性,在分析題目的過程設兩個字符串都由尾部向頭減少,而在解決問題的過程中是由頭部向尾部增加。我們可以構建一張表count[len1][len2] , 其中count[i][j] 表示如果公共子字符串在str1的第i個位置結束,在str2的第j個位置結束時公共子字符串的長度。從底向上開始構建這張表,下面是增減的描述:假設目前需要計算count[i][j],有以下幾種情況:
1. str1[i] == str2[j] : 此時count[i][j] = count[i-1][j-1]+1
2. str1[i] != str2[j] : 很顯然,以i,j為結束點的解 count[i][j] = 0 .
因此,一旦知曉了count[i-1][0,….,len2] ,count[i][0,….,len2]可以順序獲得,以str1 = “1234”, str2 = “2345”為例,表的構造方式如下:
只要在構建表的過程中記住當前最長的子字符串長度和結束位置,就可以很輕易地打印出最長公共子字符串,與之相對應的代碼如下:
| /* author : lipan date : 2013.07.29 email : areslipan@163.com*/#include <stdio.h>#include <stdlib.h>?int lcs_substring(char *str1,char *str2,int str1_len,int str2_len);int main(){ int str1_len = 0; int str2_len = 0;? printf("Please input the length of two sequence : "); scanf("%d%d",&str1_len,&str2_len); char *str1 = (char *)malloc((str1_len)*sizeof(char)); char *str2 = (char *)malloc((str2_len)*sizeof(char)); printf("Please input the two sequence:\n"); scanf("%s%s",str1,str2); lcs_substring(str1,str2,str1_len,str2_len); return 0;}?int lcs_substring(char *str1,char *str2,int str1_len,int str2_len){ //動態規劃表 char *record = (char *)malloc(str2_len*str1_len*sizeof(char)); //分別記錄當前最長的子字符串長度,截止位置 int maxSubStrLen = 0; int maxSubStrH = 0; int maxSubStrW =0; for(int h =0;h<str1_len;h++) { for(int w=0;w<str2_len;w++) { if(str1[h]==str2[w]) { if(h == 0||w == 0) { record[h*str1_len+w]=1; }? else { record[h*str1_len+w] = record[(h-1)*str1_len+w-1]+1; } if(maxSubStrLen<record[h*str1_len+w]) { maxSubStrLen = record[h*str1_len+w]; maxSubStrW= w; maxSubStrH= h; } } } } //輸出最長公共子字符串 printf("the longest common substring is :"); for(int i = maxSubStrW-maxSubStrLen+1;i<=maxSubStrW;i++) { printf("%c",str2[i] ); } return maxSubStrLen;} |
轉載于:https://www.cnblogs.com/obama/p/3223975.html
總結
- 上一篇: tomcat源码之架构解析
- 下一篇: hdu3613(扩展KMP)