再谈最长公共子串问题.docx
- 文档编号:11630963
- 上传时间:2023-03-29
- 格式:DOCX
- 页数:8
- 大小:18.21KB
再谈最长公共子串问题.docx
《再谈最长公共子串问题.docx》由会员分享,可在线阅读,更多相关《再谈最长公共子串问题.docx(8页珍藏版)》请在冰豆网上搜索。
再谈最长公共子串问题
再谈最长公共子串问题
作者:
寒雨疏桐文章来源:
网易点击数:
1049更新时间:
12/30/2003
最长公共子串(Longest common substring, 简称LCS)问题指的是求出给定的一组字符串的长度最大的共有的子字符串。
举例说明,以下三个字符串的LCS就是 cde:
abcde
cdef
ccde
高效的查找LCS算法可以用于比较多篇文章的最长相同片段,以及生物学上的基因比较等实际应用。
前几天写了一个穷举法的简单实现,感觉在数据量稍大时效率极低,所以今天上网查了一些资料,找到了解决LCS问题的最佳算法并编程实现,程序效率得到了极大的提高。
采用的是广义后缀树(Generalized Suffix Tree,简称GST)算法,就是把给定的N个源字符串的所有的后缀建成一颗树,这个树有以下一些特点:
1.树的每个节点是一个字符串,树根是空字符串“”
2.任意一个后缀子串都可以由一条从根开始的路径表达
(将这条路径上的节点字符串依次拼接起来就可以得到这个后缀)
3.特别应注意任意一个子串都可以看作某一个后缀的前缀。
既然每一个后缀
都可以由一条从根开始的路径表达,那么我们可以从根节点开始一个字符
一个字符的跟踪这条路径从而得到任意一个子串。
4.为了满足查找公共子串的需求,每个节点还应该有从属于哪个源字符串的
信息
由以上的定义我们不难看出,在这棵GST树上,如果找到深度最大并且从属于所有源字串的节点,那么,把从根到这个节点的路径上的所有节点字符串拼接起来就是LCS。
还是举例说明,上面提到的三个字符串【abcde cdef ccde】的所有后缀子串列表如下:
(注:
.1表示是从第一个串abcde来的,同理.2,.3分别表示从cdef,ccde来的)
abcde.1
bcde.1
cde.1
de.1
e.1
cdef.2
def.2
ef.2
f.2
ccde.3
cde.3
de.3
e.3
建成的GST如下图所示
(注:
.1表示从属于第一个串,.123表示既从属于第一又从属于第二,第三个源串)
--\_______【abcde.1】
|
|_____【bcde.1】 .....最深的并且带.123的节点
| :
|_____【c.123】____【de.123】_______【f.2】
| |
| |__【cde.3】
|
|_____【de.123】___【f.2】
|
|_____【e.123】____【f.2】
|
|_____【f.2】
上图中虚线所指的【de.123】节点所表示的子串cde正是LCS
以上是一些基本概念,但是实际应用时还要涉及到构建GST树以及查找LCS的具体算法,参考了网上的一些资料,我用java语言实现了这些算法,基本上可以以O(n)的时间复杂度进行建树及查找处理。
如果对构建SuffixTree算法等细节感兴趣,可以到google上查阅相关资料。
我的Java源程序如下:
*******************主程序LongestCommonSubstring.java***********
import java.io.*;
import java.util.*;
class LongestCommonSubstring {
//============================================
//Creight Suffix Tree 构建算法
//要求字符串结尾是一个在字符串中没有出现过的
//一个字符,所以选择了'\000'到'\003'等几个字符,
//如果源字串中出现了这几个特殊
//字符,程序将不能正常运行
//============================================
static String seprator[] =
{""+'\0', ""+'\1', ""+'\2', ""+'\3'};
//Build a suffix tree from a string array...
public static SuffixTreeNode buildSuffixTree(String ss[]) {
if(ss.length > seprator.length) {
System.err.println("At most "+
seprator.length+
" strings can be processing!
");
return null;
}
//Initial suffix tree...
Hashtable ht = new Hashtable();
ht.put("0", "");
SuffixTreeNode SuffixTree =
new SuffixTreeNode(-1, "", 0, ht);
//Add suffixs...
for(int i = 0; i System.err.print("Buildstring["+(i+1)+"]"); ss[i]+=seprator[i]; ht=newHashtable(); ht.put(""+(i+1),""); for(intindex=0;index if(index%1000==0) System.err.print("."); Stringstr=ss[i].substring(index); SuffixTree.insert(index,str,0,ht); } System.err.println("OK"); } returnSuffixTree; } //Readdatafromfile... publicstaticStringfile2String(Stringpathname){ StringBuffersb=newStringBuffer(); try{ FileInputStreamfis=newFileInputStream(pathname); bytebuf[]=newbyte[4096]; intn; while((n=fis.read(buf))! =-1) sb.append(newString(buf,0,n)); }catch(Exceptione){ e.printStackTrace(); } returnsb.toString(); } //Findlongestcommonsubstring... //深度优先遍历 staticString findLCSubstring(SuffixTreeNodesuffixtree, intcount) { Stringresult=""; Stringresult2; while(suffixtree! =null){ intflag=0; //count代表源字符串的个数,如果循环结束后count和flag //相等,则代表此节点从属于所有的源字串 for(inti=0;i if(suffixtree.htflag.get(""+(i+1))! =null) flag++; if(flag==count){ if(suffixtree.isLeaf()){ if(result.length()< suffixtree.label.length()) result=suffixtree.label; } else{ result2=findLCSubstring(suffixtree.child,count); if(result.length()< (suffixtree.label.length()+result2.length())) result=suffixtree.label+result2; } } suffixtree=suffixtree.next; } returnresult; } staticvoiddepthFirstTravel(SuffixTreeNodest){ SuffixTreeNodestn=st; while(stn! =null){ if(stn.child==null){ System.err.print(""); System.err.println(stn.label); }else{ System.err.print(""); System.err.print(stn.label); depthFirstTravel(stn.child); } stn=stn.next; } } staticvoidwidthFirstTravel(SuffixTreeNodest){ SuffixTreeNodestn=st; while(stn! =null){ if(stn.next==null){ System.err.print(""); System.err.println(stn.label); }else{ System.err.print(""); System.err.print(stn.label); depthFirstTravel(stn.next); } stn=stn.child; } } //Mainentry... publicstaticvoidmain(String[]args)throwsIOException{ Stringsource[]=newString[args.length]; //把名令行参数中指定的文件作为源字串读入, //注意因为结尾特殊字符资源有限(参见程序开头的seprator数组) //这个程序目前最多可以处理4个文件,如果能保证有足够多的文章 //中绝不会出现的特殊分隔符,稍微修改一下程序就可以处理更多的 //文件了 for(inti=0;i source[i]=file2String(args[i]); SuffixTreeNodest=buildSuffixTree(source); //depthFirstTravel(st.child); //System.err.println("-----------------------"); //widthFirstTravel(st.child); Stringresult; result=findLCSubstring(st.child,source.length); if(result.equals("")) System.out.println("Nocommonstring! "); else System.out.println("Thelongestcommonsubstringis: " +result+"."); } } ***************SuffixTreeNode.java************* importjava.util.*; classSuffixTreeNode{ intindex; Stringlabel; SuffixTreeNodenext; SuffixTreeNodechild; intlevel; //htflag储存从属关系信息 Hashtablehtflag=null; SuffixTreeNode(inti,Strings, intlevel,Hashtableflag){ this.index=i; this.label=s; this.level=level; if(htflag==null) htflag=newHashtable(); //Putsubject-toinformationtohtflag... htflag.putAll(flag); } booleanisLeaf(){ return(this.child==null); } voidinsert(intind,Stringstr, intlevel,Hashtableflag){ SuffixTreeNodenewnode,temp,prev; Stringstrtemp,prefix; intii; if(this.isLeaf()){ newnode=newSuffixTreeNode(ind,str, level+1,flag); this.child=newnode; return; } temp=this.child; if(temp.label.charAt(0)> str.charAt(0)) { newnode = new SuffixTreeNode(ind, str, level+1, flag); this.child = newnode; newnode.next = temp; return; } prev = temp; while ((temp ! = null) && (temp.label.charAt(0) < str.charAt(0))){ prev=temp; temp=temp.next; } if(temp==null){ newnode=newSuffixTreeNode(ind,str, level+1,flag); prev.next=newnode; return; } if(temp.label.charAt(0)> str.charAt(0)) { newnode = new SuffixTreeNode(ind, str, level+1, flag); prev.next = newnode; newnode.next = temp; return; } for (ii = 1; ii if(temp.label.charAt(ii)! = str.charAt(ii)){ break; } } if(ii==temp.label.length()){ strtemp=str.substring(ii); if(str==temp.label){ return; } temp.insert(ind,strtemp,level+1,flag); temp.htflag.putAll(flag); return; } prefix=temp.label.substring(0,ii); strtemp=temp.label.substring(ii); prev=newSuffixTreeNode(temp.index,strtemp, level+1,temp.htflag); prev.child=temp.child; temp.child=prev; temp.index=-1; temp.label=prefix; temp.htflag.putAll(flag); prev.decreaseLevel(); strtemp=str.substring(ii); temp.insert(ind,strtemp,level+1,flag); return; } voiddecreaseLevel(){ SuffixTreeNodetemp; this.level++; if(this.isLeaf()) return; temp=this.child; while(temp! =null){ temp.decreaseLevel(); temp=temp.next; } } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 最长 公共 问题