求公共子串問題以及其改進演算法
問題的提出:
設計乙個演算法,求兩個字串s1,s2的最長公共子字串的長度.例如字串"shaohui","huishao"的最長公共子字串為"shao",因此,結果為4.
最早看到這個問題,大約是2年前在csdn程式設計師雜誌的程式設計擂台上面,後來又在程式設計師考試的題目當中遇到,但是他們所使用的方法都需要消耗比較多的時間,這裡我先簡單說明一下這個問題的原始的解答方法,然後再介紹我的改進演算法.
1.以前的演算法
演算法思想:對於兩個字串s1,s2(假設字串s1長度大於字串s2的長度),設的長度為m,那麼s2的子串可以按照其長度分成m類
假設s1="shaohui",s2="ahui",則s2的子串可以分成以下幾類
4:ahui
3:ahu,hui
2:ah,hu,ui
1:a,h,u,i
然後按照長度從大到小去匹配s1,如果某個子串也是s1的子串,則找到問題的答案了.
這是我用c寫的例子程式,可以作為參考
1 #include <
iostream>
2 #include <
cstring>
3 using namespace
std;
4 /*
5 * get the length of common substring of s1 and s2
6 * if there is no common substring between s1 and s2, return 0
7 */
8
int commstr(char *s1, char *s2)
9
24 else
25
29
30
len = len1;
31 while (
len > 0)
32
43 }
44
len --;
45 }
46 found: return
len;
47 }
48
int main(int argc, char **argv)
49
說明:13-29行是保證字串s1的長度大於字串s2的長度,如果strlen(s1)
時間複雜度分析:
假設s1的長度為n,s2的長度為m, 按照最壞的打算,假設不能夠找到公共子串(也就是公共子串的長度為0)
進行的比較次數為(也就是第38行**的執行次數)
為了便於計算我們假設n=m
利用高中的數列求和的知識,很容易得到,則原時間複雜度為
o(n4)
2.我的改進演算法
原來的求解方法的時間複雜度為
o(n4),實際上還有比較大的改進餘地,原來的問題完全可以在o(n*m)的時間內得到求解.
仔細分析原來的求解的過程,對於子串s2的任意乙個長度k,字串s1和s2中的任意兩個字元之間都要進行一次比較,而當k減少1的時候,s1和s2中的任意兩個字元又要進行比較一次,這顯然是冗餘的.故如果利用以前的比較結果,時間複雜度可以降低到o(n
3).
下面具體說說我的改進演算法
將字串s1和s2分別寫在兩把直尺上面(我依然用s1,s2來表示這兩把直尺),然後將s1固定,s2的尾部和s1的頭部對齊,然後逐漸移動直尺s2,比較重疊部分的字串中的公共子串的長度,直到直尺s2移動到s1的尾部.在這個過程中求得的最大長度就是s1,s2最大子串的長度.
下圖是求解過程的圖示,藍色部分表示重疊的字串,紅色的部分表示重疊部分相同的子串
其中s1="shaohui",s2="ahui",最後的求得的結果為3
按照這個思想,很容易得到這個例子程式
1 #include <
iostream>
2 #include <
string.h>
3 using namespace
std;
4
int commstr(char *s1, char *s2)
5
27 }
28 max =
curmax > max ? curmax : max;
29 }
30
31 return max;
32 }
33
int main(int argc, char **argv)
34
時間複雜度分析:
容易計算,時間複雜度o(n,m)=(n+m)m
令n=m
則時間複雜度
o(n)=n2,
與以前的演算法相比較,降低了2次,應該算是比較大的改進了
3.遞迴的方法
我用python寫了乙個遞迴的求解方法,如下
def commstr(long,short) :
if short in long :
return len(short)
return max(commstr(long,short[:-1]),commstr(long,short[1:]))
print commstr('shaohui','huishao')
**很簡單,原理也很簡單,儘管是使用的遞迴,但是基本思想還是以前的求解方法:對於字串a,b,盡量用b的最大的子字串c去匹配另外乙個字串a,如果c不是a的子串,那麼用字串b的長度比b少1的子串去匹配a,直到匹配到了為止或者子串的長度為0.
為了便於參考,我也用c寫了個,不用解釋了,因為注釋已經很清楚了
#include
#include
#define buffer_size 255
int commstr(char *s1, char *s2)
if (cnt >= len2)//如果s2是s1的子串
return len2;
//把s2中後len2-1個字元構成的串同s1比較,並求字串長度
len1 = commstr(s1,s2+1);
//把s2中前len2-1個字元構成的串同s1比較,並求字串長度
strncpy(buf,s2,len2-1);
buf[len2-1] = '/0';
len2 = commstr(s1,buf);
//返回較大者
return len1 > len2 ? len1 : len2;
}
int main(int arc, char *argv)
不知道是否有更好的演算法,我一直在找尋.
求公共子串行和公共子串
輸入 第一行給出乙個整數n 0最長公共子串行長度。每組結果佔一行。樣例輸入 2asdf adfsd 123abc abc123abc 樣例輸出36 輸入兩個字串,輸出它們的最長子字串。求公共子串以及求公共子串的長度 include include define n 100 char a n b n ...
LCS演算法求最長公共子串
一 問題描述 如果字串一的所有字元按其在字串中的順序出現在另外乙個字串二中,則字串一稱之為字串二的子串。注意,並不要求子串 字串一 的字元必須連續出現在字串二中。請編寫乙個函式,輸入兩個字串,求它們的最長公共子串,並列印出最長公共子串。例如 輸入兩個字串bdcaba和abcbdab,字串bcba和b...
求最長公共子串
參考 最長公共子串 longest common substirng 和最長公共子串行 longest common subsequence,lcs 的區別為 子串是串的乙個連續的部分,子串行則是從不改變序列的順序,而從序列中去掉任意的元素而獲得新的序列 也就是說,子串中字元的位置必須是連續的,子串...