`
yiminghe
  • 浏览: 1466425 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

LCS 问题

阅读更多
/**
 * Author: yiminghe
 * Date: 2008-10-24
 * Time: 15:08:32
 * Any problem ,contact yiminghe@fudan.edu.cn.
 */

import java.io.*;
import java.util.*;

/**
 * 利用 后缀树算法
 *
 * LCS 问题及其扩展,找到多个字符串的所有公共子串
 *
 */
class LCS {
    public static void main(String[] args) throws IOException {
        String[] source = {"axybcde", "cdefxy", "xyccde"};

        SuffixTreeNode st = buildSuffixTree(source);

        String result;
        result = Lcs(st.firstChild, source.length);

        if (result.equals(""))
            System.out.println("No common string!");
        else
            System.out.println("The longest common substring is : "
                    + result + " .");


        String[] commons = commonString(source);
        System.out.println(Arrays.asList(commons));
    }

    /**
     * 建立一个后缀树
     *
     * @param ss 字符串数组
     * @return 后缀树的根结点
     */
    public static SuffixTreeNode buildSuffixTree(String ss[]) {
        HashMap<String, String> belong = new HashMap<String, String>();
        belong.put("0", "");
        SuffixTreeNode SuffixTree =
                new SuffixTreeNode(-1, "", 0, belong);

        //Add suffixs...
        for (int i = 0; i < ss.length; i++) {
            System.err.print("后缀树[" + (i + 1) + "]");

            belong = new HashMap<String, String>();
            belong.put("" + (i + 1), "");
            for (int index = 0; index < ss[i].length(); index++) {
                String str = ss[i].substring(index);
                SuffixTree.insert(index, str, 0, belong);
            }
            System.err.println("  -  OK");
        }

        return SuffixTree;
    }

    /**
     * 深度遍历
     *
     * @param suffixtree 根的后缀树结点
     * @param count      字符串总数
     * @return 最长公共子串
     */
    public static String Lcs(SuffixTreeNode suffixtree, int count) {
        String result = "";
        String result2;
        while (suffixtree != null) {
            int flag = suffixtree.belongTo.size();

            if (flag == count) {
                if (suffixtree.isLeaf()) {
                    //找到最大
                    if (result.length() <
                            suffixtree.label.length())
                        result = suffixtree.label;
                } else {
                    //只是后缀的后缀
                    result2 = Lcs(suffixtree.firstChild, count);
                    //要完整的后缀
                    if (result.length() <
                            (suffixtree.label.length() + result2.length()))
                        result = suffixtree.label + result2;
                }
            }
            suffixtree = suffixtree.next;
        }
        return result;
    }


    /**
     * 找到所有的相同子串,子串间不相互包含
     *
     * @param source 字符串集合
     * @return 字符串集合
     */
    public static String[] commonString(String[] source) {
        HashSet<String> r = new HashSet<String>();
        SuffixTreeNode st = buildSuffixTree(source);
        recurCommon(r, st.firstChild, source.length);
        String[] original = r.toArray(new String[r.size()]);
        ArrayList<String> result = new ArrayList<String>();
        for (int i = 0; i < original.length; i++) {
            int j = 0;
            for (j = 0; j < original.length; j++) {

                //有和其它元素相互包含 ,舍弃
                if (i != j && original[j].endsWith(original[i])) {
                    break;
                }
            }

            if (j == original.length) {
                result.add(original[i]);
            }
        }

        return result.toArray(new String[result.size()]);
    }


    //搜集子串,并且去掉明显的嵌套子串
    private static boolean recurCommon(HashSet<String> r, SuffixTreeNode suffixtree, int count) {
        boolean result = false;
        while (suffixtree != null) {
            int flag = suffixtree.belongTo.size();

            if (flag == count) {
                result = true;
                if (suffixtree.isLeaf()) {
                    String re = suffixtree.label;
                    SuffixTreeNode temp = suffixtree;
                    while (temp.parent != null) {
                        temp = temp.parent;
                        re = temp.label + re;
                    }

                    r.add(re);

                } else {
                    //只是后缀的后缀
                    boolean has = recurCommon(r, suffixtree.firstChild, count);
                    //要完整的后缀
                    if (!has) {
                        String re = suffixtree.label;
                        SuffixTreeNode temp = suffixtree;
                        while (temp.parent != null) {
                            temp = temp.parent;
                            re = temp.label + re;
                        }

                        r.add(re);

                    }
                }
            }
            suffixtree = suffixtree.next;
        }
        return result;
    }


}

class SuffixTreeNode {
    //原字符串的位置
    //公共就没意义了
    int index;
    //后缀值
    String label;
    //兄弟关系
    SuffixTreeNode next;
    //第一个孩子关系
    SuffixTreeNode firstChild = null;
    //父亲
    SuffixTreeNode parent = null;
    //树的层数
    int level;

    //属于哪个字符串
    HashMap<String, String> belongTo = null;

    SuffixTreeNode(int i, String s,
                   int level, HashMap<String, String> flag) {
        this.index = i;
        this.label = s;
        this.level = level;

        if (belongTo == null)
            belongTo = new HashMap<String, String>();
//Put subject-to information to belongTo...
        belongTo.putAll(flag);
    }


    void setChilden(SuffixTreeNode n) {
        this.firstChild = n;
        if (n != null)
            n.parent = this;
    }

    boolean isLeaf() {
        return (this.firstChild == null);
    }

    /**
     * 在当前结点下插入 新的后缀树结点
     *
     * @param ind    index
     * @param str    insert_str
     * @param level  level
     * @param belong belong
     */
    public void insert(int ind, String str,
                       int level, HashMap<String, String> belong) {
        SuffixTreeNode newnode, firstChild, prev;
        String strtemp, prefix;
        int index_i;

        //第一次   只有根结点
        if (this.isLeaf()) {
            newnode = new SuffixTreeNode(ind, str,
                    level + 1, belong);
            this.setChilden(newnode);
            return;
        }

        firstChild = this.firstChild;

        if (firstChild.label.charAt(0) > str.charAt(0)) {
            newnode = new SuffixTreeNode(ind, str,
                    level + 1, belong);
            this.setChilden(newnode);
            newnode.next = firstChild;
            return;
        }
        prev = firstChild;

        //合适的子结点插入位置
        while ((firstChild != null) &&
                (firstChild.label.charAt(0) <
                        str.charAt(0))) {
            prev = firstChild;
            firstChild = firstChild.next;
        }
        if (firstChild == null) {
            newnode = new SuffixTreeNode(ind, str,
                    level + 1, belong);
            newnode.parent = this;
            prev.next = newnode;
            return;
        }
        if (firstChild.label.charAt(0) > str.charAt(0)) {
            newnode = new SuffixTreeNode(ind, str,
                    level + 1, belong);
            prev.next = newnode;
            newnode.parent = this;
            newnode.next = firstChild;
            return;
        }
        //与 str 完全相同
        if (str.equals(firstChild.label)) {
            //公共前缀属性共有
            firstChild.belongTo.putAll(belong);
            return;
        }
        //首字母相同
        int minLength = Math.min(firstChild.label.length(), str.length());
        for (index_i = 1; index_i < minLength; index_i++) {
            if (firstChild.label.charAt(index_i) !=
                    str.charAt(index_i)) {
                break;
            }
        }

        //temp 较短 ,或与 str 完全相同
        if (index_i == firstChild.label.length()) {


            //str 比 temp 长的部分
            strtemp = str.substring(index_i);
            firstChild.insert(ind, strtemp, level + 1, belong);
            //公共前缀属性共有
            firstChild.belongTo.putAll(belong);
            return;
        }

        //str 较短,或者 与 temp 中间 有不同元素
        //原来的 temp 前缀 共有
        prefix = firstChild.label.substring(0, index_i);
        strtemp = firstChild.label.substring(index_i);

        //原来 temp 的 与 str 不同的后缀 分离
        prev = new SuffixTreeNode(firstChild.index, strtemp,
                level + 1, firstChild.belongTo);
        prev.setChilden(firstChild.firstChild);

        firstChild.setChilden(prev);
        firstChild.index = -1;
        firstChild.label = prefix;
        firstChild.belongTo.putAll(belong);
        prev.lowDown();


        //加入 原来 str 与 temp 不同的后缀
        if (index_i < str.length()) {
            strtemp = str.substring(index_i);

            firstChild.insert(ind, strtemp, level + 1, belong);
        }

    }


    void print() {

    }

    /**
     * 加入中间树结点,对原树结点中保存的层次信息进行刷新
     */
    void lowDown() {
        SuffixTreeNode temp;
        this.level++;
        if (this.isLeaf())
            return;
        temp = this.firstChild;
        while (temp != null) {
            temp.lowDown();
            temp = temp.next;
        }
    }
}


分享到:
评论

相关推荐

    动态规划解决LCS问题

    在压缩包文件"dynamic programming"中,可能包含了一些示例代码或进一步解释动态规划解决LCS问题的资料,这些资源可以帮助理解并实践上述理论知识。学习动态规划不仅有助于解决LCS问题,还可以帮助解决其他类似的...

    实验九lcs算法.doc

    LCS问题的基本定义是:给定两个序列X和Y,找出它们的最长公共子序列,这个子序列不必连续,但要求字符顺序与原序列相同。例如,序列X = "ABCDGH"和Y = "AEDFHR"的一个LCS是"ADH"。 2. 解析: LCS问题可以使用动态...

    lcs.rar_ateh8z_lcs问题_最长公共子序列;动态规划;计算生物学;

    LCS问题就是求两个字符串最长公共子串的问题。解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1的序列,其对应的位置就是最长匹配子串的位置...

    LCS.rar_LCS 子问题层叠

    LCS问题常用于比较和分析文本、生物序列比对等场景。 标题"LCS.rar_LCS 子问题层叠"暗示了我们关注的重点是LCS问题的动态规划解决方案,特别是如何通过自底向上的方法解决这个问题。在动态规划中,子问题层叠通常...

    最长公共子序列LCS(c++)

    使用c++语言编写的LCS问题的求解过程

    最长公共子序列问题LCS

    LCS问题的描述是:给定两个序列X=, x2, …, xm&gt;和Y=, y2, …, yn&gt;,要求找出X和Y的最长公共子序列。所谓公共子序列是指可以从原序列中删除一些元素得到的序列。例如,序列Z=, C, D, B&gt;是序列X=, B, C, B, D, A, B&gt;的...

    最长子序列LCS算法

    两个序列的LCS问题包含两个序列的前缀的LCS,因此,LCS问题具有最优子结构性质。在设计递归算法时,不难看出递归算法具有子问题重叠的性质。   设C[i,j]C[i,j]表示XiXi和YjYj的最长公共子序列LCS的长度。如果i=0i...

    LCS.rar_LCS_lcs dynamic

    在这个案例中,我们将深入探讨LCS问题以及如何用C语言来实现。 **最长公共子序列问题(LCS)** 是在两个序列中寻找最长的子序列,这个子序列在两个序列中都存在,但不一定连续。LCS在生物信息学、文本比较、计算机...

    lcs.rar_LCS_lcs dynamic

    动态规划是解决LCS问题的一种高效方法,因为它避免了重复计算,从而优化了时间复杂度。以下将详细介绍动态规划如何应用于LCS的求解。 ### 动态规划思路 动态规划通常使用二维数组`dp`来存储中间结果,`dp[i][j]`...

    LCS.rar_LCS

    解决LCS问题的经典方法是采用动态规划,这种策略将问题分解为更小的子问题,然后通过构建一个二维数组来存储这些子问题的解。这个二维数组通常称为LCS表,它的大小为m+1(m为字符串A的长度)乘以n+1(n为字符串B的...

    LCS.rar_LCS_lcs algorithm_最长公共子序列

    在本压缩包文件"LCS.rar"中,包含了一个名为"LCS"的程序,该程序可能用C++实现了KMP算法来解决LCS问题。 LCS问题的基本概念是:给定两个序列X和Y,找出它们的最长子序列,这个子序列并不一定是连续的,但要在两个...

    lcs.rar_LCS

    在压缩包中的“06083115-郭康-最大子序列问题”可能是一个文件名,可能包含郭康(假设是作者)编写的代码或报告,详细解释了LCS问题的解决方法,并可能提供了执行示例,帮助读者直观地理解LCS算法的运行过程。...

    LCS.zip_LCS 复杂度分析

    描述中的“用后缀自动机在线性时间复杂度下解决LCS问题”提到了一种高效的算法方法,即利用后缀自动机(Suffix Automaton)来解决LCS问题。后缀自动机是一种特殊的有限状态自动机,它能够快速处理字符串的后缀查询,...

    LCS的实现代码

    在LCS问题中,我们可以用一个二维数组dp[i][j]来表示字符串s1的前i个字符和字符串s2的前j个字符的最长公共子序列的长度。 2. **状态转移方程**:LCS的关键在于确定dp数组的状态转移方程。如果s1的第i个字符与s2的第...

    LCS.rar_直接递归LCS c

    LCS问题的核心是找出两个字符串之间的最长子序列,这个子序列不必连续,但必须保持原字符顺序。 LCS问题可以被定义为:给定两个字符串S和T,找到它们的最长公共子序列。如果S = "ABCDEF",T = "ACDFE",那么它们的...

Global site tag (gtag.js) - Google Analytics