AC解 - Life Forms(POJ#3294)

来源:互联网 发布:网龙网络怎么样 编辑:程序博客网 时间:2024/06/06 00:16

原题: http://poj.org/problem?id=3294

或者:http://acm.nankai.edu.cn/p1312.html


Description

You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits such as height, colour, wrinkles, ears, eyebrows and the like. A few bear no human resemblance; these typically have geometric or amorphous shapes like cubes, oil slicks or clouds of dust.

The answer is given in the 146th episode of Star Trek - The Next Generation, titled The Chase. It turns out that in the vast majority of the quadrant's life forms ended up with a large fragment of common DNA.

Given the DNA sequences of several life forms represented as strings of letters, you are to find the longest substring that is shared by more than half of them.

Input

Standard input contains several test cases. Each test case begins with 1 ≤ n ≤ 100, the number of life forms. n lines follow; each contains a string of lower case letters representing the DNA sequence of a life form. Each DNA sequence contains at least one and not more than 1000 letters. A line containing 0 follows the last test case.

Output

For each test case, output the longest string or strings shared by more than half of the life forms. If there are many, output all of them in alphabetical order. If there is no solution with at least one letter, output "?". Leave an empty line between test cases.

Sample Input

3abcdefgbcdefghcdefghi3xxxyyyzzz0

Sample Output

bcdefgcdefgh?


分析:本题跟Longest Commong Substring的解法类似,也是求出n个字符串的最长公共子串,只不过本题中要求n的值只需要超过半数即可(而不是所有字符串的公共子串),另外如果有多个等长的公共子串,需要要同时按字典顺序输出。

首先构造n个DNA序列合成的字符串的后缀树,然后采用深度优先遍历算法(这里使用后序遍历)找出满足条件的最长公共子串。在深度优先算法中,只需要考虑内部结点下是否有过半的不同后缀(过半后缀个数变量名为dominatedCount)作为它的后代叶子结点。在任何一个内部结点Node中,如果它的所有儿子结点都不满足含过半后缀的条件,就需要重新统计Node自身是否满足该条件,否则只要有任何一个儿子结点满足条件,结点Node就不需要考虑了,因为Node的深度肯定比满足条件的儿子结点小。在具体实现中,需要在每个节点中添加一个标志done来标记该结点是否已经被处理。由于使用了另一篇文中介绍的mcc算法构造后缀树,输出的多个等长的公共子串(如果存在多个的话)已经是字典顺序。 另外,结点的深度就是pathlen属性。


代码:

import java.util.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.Scanner;import java.util.Stack;/** *  * POJ#3294: Life Forms *   * Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/) * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)  *  * @author ljs * 2011-07-04 * */public class Main {private class SuffixNode {private String text;    private List<SuffixNode> children = new LinkedList<SuffixNode>();        private SuffixNode link;    private int start;    private int end;    private int pathlen;        private boolean done;        public SuffixNode(String text,int start,int end,int pathlen){    this.text = text;    this.start = start;    this.end = end;    this.pathlen = pathlen;    }    public SuffixNode(String text){        this.text = text;    this.start = -1;    this.end = -1;        this.pathlen = 0;    }    public int getLength(){    if(start == -1) return 0;    else return end - start + 1;    }    public boolean isRoot(){    return start == -1;    }    public String getCoordinate(){    return "[" + start+".." + end + "/" + this.pathlen + "]";    }    public String getString(){    if(start != -1){    return this.text.substring(start,end+1);    }else{    return "";    }    }    public String toString(){    return this.getString() + getCoordinate() + "["+ this.done + "]";    }}private class State{private SuffixNode u; //parent(head)private SuffixNode w; //s(head[i-1])private SuffixNode v; //head[i-1]private int j; //the global index of text starting from 0 to text.length()private boolean finished; //is this suffix insertion finished?}private SuffixNode root;private String text;private int[] termpos;private int dominatedCount;public Main(String text,int[] termpos){this.text = text;this.termpos = termpos;dominatedCount = termpos.length/2 + 1;}//build a suffix-tree for a string of textprivate void buildSuffixTree() throws Exception{if(root==null){root = new SuffixNode(text);root.link = root; //link to itself}SuffixNode u = root;SuffixNode v = root;State state = new State();for(int i=0;i<text.length();i++){//process each suffixSuffixNode s = u.link;int uvLen=v.pathlen - u.pathlen;  if(u.isRoot() && !v.isRoot()){uvLen--;}int j = s.pathlen + i;//init statestate.u = s;state.w = s; //if uvLen = 0state.v = s;state.j = j;state.finished = false;//execute fast scanif(uvLen > 0) {fastscan(state,s,uvLen,j);}//establish the suffix link with vSuffixNode w = state.w;v.link = w;//execute slow scanif(!state.finished){j = state.j;state.u = w; //w must be an internal node when state.finished=false, then it must have a suffix link, so u can be updated.slowscan(state,w,j);}u = state.u;v = state.v;}}//slow scan until head(=state.v) is foundprivate void slowscan(State state,SuffixNode currNode,int j){boolean done = false;int keyLen = text.length() - j;for(int i=0;i<currNode.children.size();i++){SuffixNode child = currNode.children.get(i);//use min(child.key.length, key.length)int childKeyLen = child.getLength();int len = childKeyLen<keyLen?childKeyLen:keyLen;int delta = 0;for(;delta<len;delta++){if(text.charAt(j+delta) != text.charAt(child.start+delta)){break;}}if(delta==0){//this child doesn't matchany character with the new key//order keys by lexi-orderif(text.charAt(j) < text.charAt(child.start)){//e.g. child="e" (currNode="abc")//   abc                     abc//    /  \    =========>      / | \//   e    f   insert "c^"    c^ e  fint pathlen = text.length() - j + currNode.pathlen;SuffixNode node = new SuffixNode(text,j,text.length()-1,pathlen);currNode.children.add(i,node);//state.u = currNode; //currNode is already registered as state.u, so commented outstate.v = currNode;state.finished = true;done = true;break;}else{ //key.charAt(0)>child.key.charAt(0)//don't forget to add the largest new key after iterating all childrencontinue;}}else{//current child's key partially matches with the new keyif(delta==len){if(keyLen>childKeyLen){ //suffix tree with ^ ending can't have other two cases//e.g. child="ab"//   ab                      ab//    /  \    ==========>     / | \ //   e    f   insert "abc^"  c^ e  f//recursionstate.u = child;j += childKeyLen;state.j = j;slowscan(state,child,j);}}else{//0<delta<len //e.g. child="abc"//   abc                     ab//    /  \     ==========>     / \//   e    f   insert "abd^"   c  d^ //                           /  \//                          e    f//insert the new node: ab int nodepathlen = child.pathlen - (child.getLength()-delta);SuffixNode node = new SuffixNode(text,child.start,child.start + delta - 1,nodepathlen); node.children = new LinkedList<SuffixNode>();int tailpathlen = (text.length() - (j + delta)) + nodepathlen;SuffixNode tail = new SuffixNode(text,j+delta,text.length()-1,tailpathlen);//update child node: cchild.start += delta;if(text.charAt(j+delta)<text.charAt(child.start)){node.children.add(tail);node.children.add(child);}else{node.children.add(child);node.children.add(tail);}//update parentcurrNode.children.set(i, node);//state.u = currNode; //currNode is already registered as state.u, so commented outstate.v = node;state.finished = true;}done = true;break;}}if(!done){int pathlen = text.length() - j + currNode.pathlen;SuffixNode node = new SuffixNode(text,j,text.length()-1,pathlen);currNode.children.add(node);//state.u = currNode; //currNode is already registered as state.u, so commented outstate.v = currNode;state.finished = true;}}//fast scan until w is foundprivate void fastscan(State state,SuffixNode currNode,int uvLen,int j){  for(int i=0;i<currNode.children.size();i++){SuffixNode child = currNode.children.get(i);if(text.charAt(child.start) == text.charAt(j)){int len = child.getLength();if(uvLen==len){//then we find w//uvLen = 0;//need slow scan after this childstate.u = child;state.w = child;state.j = j+len;}else if(uvLen<len){//branchingand cut child short//e.g. child="abc",uvLen = 2//   abc                          ab//    /  \    ================>     / \//   e    f   suffix part: "abd^"  c   d^ //                                /  \//                               e    f//insert the new node: ab; child is now c int nodepathlen = child.pathlen - (child.getLength()-uvLen);SuffixNode node = new SuffixNode(text,child.start,child.start + uvLen - 1,nodepathlen); node.children = new LinkedList<SuffixNode>();int tailpathlen = (text.length() - (j + uvLen)) + nodepathlen;SuffixNode tail = new SuffixNode(text,j+uvLen,text.length()-1,tailpathlen);//update child node: cchild.start += uvLen;if(text.charAt(j+uvLen)<text.charAt(child.start)){node.children.add(tail);node.children.add(child);}else{node.children.add(child);node.children.add(tail);}//update parentcurrNode.children.set(i, node);//uvLen = 0;//state.u = currNode; //currNode is already registered as state.u, so commented outstate.w = node;state.finished = true;state.v = node;}else{//uvLen>len//e.g. child="abc", uvLen = 4//   abc                          //    /  \    ================>      //   e    f   suffix part: "abcdefg^"   //                                //                  //jump to next nodeuvLen -= len;state.u = child;j += len;state.j = j;fastscan(state,child,uvLen,j);}break;}}}public List<List<SuffixNode>> solve(){List<List<SuffixNode>> paths = new ArrayList<List<SuffixNode>>();Stack<SuffixNode> stack = new Stack<SuffixNode>();stack.push(root);traverse(root,0,paths,stack);//for(List<SuffixNode> path:paths){//System.out.format("%s%n",path);//}return paths;}public int traverse(SuffixNode currNode,int maxDepth,List<List<SuffixNode>> paths,Stack<SuffixNode> stack){//post order traversefor(int i=0;i<currNode.children.size();i++){SuffixNode child = currNode.children.get(i);stack.push(child);maxDepth = traverse(child,maxDepth,paths,stack);}//visit currNodeif(!currNode.children.isEmpty()){boolean accepted = false;for(int i=0;i<currNode.children.size();i++){SuffixNode child = currNode.children.get(i);if(child.done){accepted = true;break;}}if(accepted){//if any one of the children is done, then current node is //out of considerationcurrNode.done = true;}else{//if all of the children is not done, then current node should //be consideredif(containTerminators(currNode)){int depth = currNode.pathlen;if(depth>maxDepth){maxDepth = depth;paths.clear();paths.add(new ArrayList<SuffixNode>(stack)); //register the path//System.out.format("%s%n",path);}else if(depth==maxDepth){paths.add(new ArrayList<SuffixNode>(stack)); //register the path//System.out.format("%s%n",path);}currNode.done = true;}}}//else{//currNode.done = false;//}stack.pop();return maxDepth;}private boolean containTerminators(SuffixNode currNode){boolean[] done = new boolean[termpos.length];return containTerminators(currNode,done);}private boolean containTerminators(SuffixNode currNode,boolean[] done){for(int i=0;i<currNode.children.size();i++){SuffixNode child = currNode.children.get(i);if(child.children.isEmpty()){//leaf node//Note: here the order of terminator is important for(int j=0;j<termpos.length;j++){if(termpos[j] >= child.start && termpos[j] <= child.end){done[j]=true;break;}}}else{containTerminators(child,done);}}int k=0;for(int j=0;j<done.length;j++){if(done[j]){k++;}}if(k>=dominatedCount){return true;}elsereturn false;}//for test purpose onlypublic void printTree(){System.out.format("The suffix tree for S = %s is: %n",this.text);this.print(0, this.root);}private void print(int level, SuffixNode node){for (int i = 0; i < level; i++) {            System.out.format(" ");        }System.out.format("|");        for (int i = 0; i < level; i++) {        System.out.format("-");        }        System.out.format("%s(%d..%d/%d,%s)%n", node.getString(),node.start,node.end,node.pathlen,node.done);        //System.out.format("(%d,%d)%n", node.start,node.end);        for (SuffixNode child : node.children) {        print(level + 1, child);        }}public static void main(String[] args) throws Exception {Scanner cin = new Scanner(System.in);String line = cin.nextLine();line = line.trim();int lifeformsCount = Integer.parseInt(line);List<String[]> testcases = new ArrayList<String[]>();while(lifeformsCount>0){String[] lifeforms = new String[lifeformsCount];for(int i=0;i<lifeformsCount;i++){line = cin.nextLine();line = line.trim();lifeforms[i] = line;}testcases.add(lifeforms);line = cin.nextLine();line = line.trim();lifeformsCount = Integer.parseInt(line);}//testfor(int k=0;k<testcases.size();k++){String[] lifeforms = testcases.get(k);int count = lifeforms.length;if(count==1){System.out.println(lifeforms[0]);}else{char[] terminators = new char[count];char t = '\u0100';for(int i=t,j=0;j<count;i++,j++){terminators[j] = (char)i;}int[] termpos = new int[count];StringBuilder sb = new StringBuilder();for(int j=0;j<count;j++){sb.append(lifeforms[j]);sb.append(terminators[j]);termpos[j] = sb.length()-1;}Main stree = new Main(sb.toString(),termpos);stree.buildSuffixTree();//stree.printTree();List<List<SuffixNode>> paths = stree.solve();//stree.printTree();for(List<SuffixNode> path:paths){if(path.size()==1){ //only rootSystem.out.println("?");break;}StringBuilder sbgenes = new StringBuilder();for(int i=1;i<path.size();i++){SuffixNode node = path.get(i);sbgenes.append(node.getString());}System.out.println(sbgenes);}}if(k<testcases.size()-1)System.out.println();}}}


测试数据(注意n可以取值等于1,1 ≤ n ≤ 100):

3
abcdefg
bcdefgh
cdefghi
3
xxx
yyy
zzz
1
abc
2
aaa
aaaa
0


测试输出:
bcdefg
cdefgh

?

abc

aaa