經(jīng)典的求公共子序列算法需要兩個序列的長度已知.而且通常用于計算字符串的公共子序列.
我實(shí)現(xiàn)的算法針對原有的算法輸入需求解耦合,使得算法極度可適配.能用于字符串公共子序列計算和文件diff計算.理論上能用于任何具備相似特征的兩個序列的公共子序列計算.
LCS_Calculate有三個變種:
template<typename L_Iterator,typename R_Iterator,typename Container>

LCS_Size FEtool::LCS_Calculate(L_Iterator lbeg,L_Iterator lend, R_Iterator rbeg,R_Iterator rend,Container &out);

template<typename L_Iterator,typename R_Container,typename Container>

inline LCS_Size FEtool::LCS_Calculate(L_Iterator lbeg,L_Iterator lend, R_Container const&rcontainer, Container &out);

template<typename L_Container,typename R_Container,typename Container>

inline LCS_Size FEtool::LCS_Calculate(L_Container const& lcontainer, R_Container const&rcontainer, Container &out);


L_Iterator接受輸入迭代器. R_Iterator接受隨機(jī)迭代器. L_Container和R_Container分別調(diào)用它們的begin()和end()方法轉(zhuǎn)調(diào)用到LCS_Calculate的第一個版本.
L_*打頭的指代比較序列中左邊那個,R_*打頭的指代比較序列中右邊那個.
最后一個參數(shù)Container&out接收一個容器用來輸出序列各段相同的地方.典型的Container參數(shù)為std::deque<FEtool::SectionCommon> section;也可以為FEtool:: EmptyContainer.
class EmptyContainer


{
public:

void push_back(SectionCommon const&)
{};

LCS_Size size()
{return 0;}

void clear()
{}
};
如果為FEtool:: EmptyContainer參數(shù)則通過模板特化代碼選擇不計算兩段序列的相同部分。
struct LCS_Compare_Trait


{
template<typename L,typename R>
static bool equal(L const& left, R const& right)

{
return left==right;
}
};
定義了比較算法,默認(rèn)用operator==.你可以在FEtool空間通過特化或偏特化LCS_Compare_Trait:: equal來定制它.
struct SectionCommon


{
LCS_Size L_begin;
LCS_Size R_begin;
LCS_Size count;

void clear()
{L_begin=0;R_begin=0;count=0;}
};
指示兩個序列的相同部分. 比如SectionCommon:: L_begin為0, SectionCommon:: R_begin為10, SectionCommon::count為5.就表示左邊序列從0開始的5個數(shù)據(jù),和右邊序列從10開始的5個數(shù)據(jù)都相同.
LCS_Calculate內(nèi)部根據(jù)傳入?yún)?shù)優(yōu)化實(shí)現(xiàn).經(jīng)過對經(jīng)典的動態(tài)規(guī)劃解公共子序列算法的考察發(fā)現(xiàn),外圍那個循環(huán)只需要遍歷它代表的序列一次;即左邊序列則滿足輸入迭代器即可.它要求右邊序列始終是傳入隨機(jī)迭代器.內(nèi)部計算用到的數(shù)組使用了滾動數(shù)組(LCSArray)實(shí)現(xiàn),空間占用為右邊序列長度*2.
LCS_Calculate的最后一個參數(shù)不為EmptyContainer則會計算公共子序列在左右序列中各段的順序和長度.這里L_Iterator是不是隨機(jī)訪問迭代器就會影響到性能了.L_Iterator不是隨機(jī)迭代器內(nèi)部就會用到一個動態(tài)增長的輔助數(shù)組(TrackArrayDynamic)來做回溯; L_Iterator是隨機(jī)迭代器則直接一次申請(左序列*右序列)這么大的空間(TrackArrayStatic)來輔助回溯計算.
而如果LCS_Calculate的最后一個參數(shù)為EmptyContainer則會選擇一個空數(shù)組(TrackArrayEmpty)實(shí)現(xiàn).TrackArrayEmpty類把所有操作展開為空操作.
所有這些,基于模板來自動選擇.用戶不需要指定不同的函數(shù)來優(yōu)化性能:

template<typename L_Iterator,typename R_Iterator,typename Container/**//*vector<LCS_Section>*/>
LCS_Size LCS_Calculate(L_Iterator lbeg,L_Iterator lend,
R_Iterator rbeg,R_Iterator rend,
Container &out)


{
out.clear();
detail::LCSArray array(rend-rbeg);
typedef detail::SelectTrackArray<Container,typename std::iterator_traits<L_Iterator>::iterator_category> SelectTrack;//選擇適當(dāng)?shù)幕厮輸?shù)組
typename SelectTrack::Array trackArr(SelectTrack::TotalRows(lbeg,lend),array.columns());//選擇適當(dāng)?shù)幕厮輸?shù)組
LCS_Size leftSize;
LCS_Size rightSize;
for( leftSize=1;lbeg!=lend;++lbeg,++leftSize)//外層只需要是輸入迭代器就可
for( rightSize=1;rightSize<=array.columns();++rightSize)

{

if(LCS_Compare_Trait::equal(*lbeg,*(rbeg+rightSize-1)))
{
array(leftSize,rightSize)=array(leftSize-1,rightSize-1)+1;
trackArr(leftSize,rightSize)=0;
}

else if(array(leftSize-1,rightSize)>=array(leftSize,rightSize-1))
{
array(leftSize,rightSize)=array(leftSize-1,rightSize);
trackArr(leftSize,rightSize)=1;
}

else
{
array(leftSize,rightSize)=array(leftSize,rightSize-1);
trackArr(leftSize,rightSize)=-1;
}

}
detail::LCS_KeepTrack(trackArr,out);

return array(leftSize-1,array.columns());

}
完整代碼包括測試代碼下載