最長句子

題目描述

  英語中,有些單詞能夠出如今其餘單詞後面。例如「Love」能夠出如今「I」以後,「You」能夠出如今「Love」以後,所以它們能構成「I Love You」這句話。
  如今給你一些單詞間的關係,你能計算出最多能有幾個單詞組合在一塊兒構成一句話嗎?java

1.1 輸入描述:

  輸入包含多組數據。
  每組數據的第一行包含一個正整數n (1≤n≤10000)。
  緊接着n行單詞關係,每行包含兩個單詞A和B,表示單詞B能出如今A後面。單詞長度不超過32個字符,而且只有字母組成。
  不存在循環的依賴關係。算法

1.2 輸出描述:

  對應每組數據,輸出最多有幾個單詞能構成一個句子。數組

1.3 輸入例子:

1
hello world
3
I love
love you
love me

 

1.4 輸出例子:

2
3

 

2 解題思路

2.1 步驟一

  假設某一組數據有n條,第i條記錄爲(Ini,Outi),當Outi=Inj時,能夠將(Ini,Outi)、(Inj,Outj)結合成(Ini,Outi,Outj),它們出現的前後表示有向關係,不存在循環的依賴關係。依據這個的思路能夠將全部的(Ini,Outi)記錄構形成一個有向無環圖。IniOuti表示圖中的頂點,(Ini,Outi)表示有向邊。
假設有輸入記錄:(A,B)、(B,C)、(C,D)、(B,D)、(E,F)、(F,G)、(C,E)、(I,B)、(A,F)。根據輸入的前後順序構造一個有向圖,有向圖的構造如圖2-1所示。
這裏寫圖片描述
  圖2-1 根據輸入構造有向無環圖
  根據輸入的添加過程,能夠知道構造有向無環圖的過程。假設G是有向圖,V是圖G的頂點集合。對於某一個輸入序列(Ini,Outi)。分四種狀況:
  若是IniOuti都在V中,則在圖G中,添加IniOuti的有向邊。
  若是隻有Ini在G中,則在圖G中添加新的頂點Outi和有向邊(Ini,Outi),而且將Outi加入到V中。
  若是隻有Outi在G中,則在圖G中添加新的頂點In_i和有向邊(Ini,Outi),而且將Ini加入到V中。
  若是IniOuti都不在V中,則在圖G中添加新的頂點IniOuti和有向邊(Ini,Outi),而且將IniOuti加入到V中。
  對全部的輸入序列(Ini,Outi)都進行上面的操做,最後構形成一個有向無環圖G。圖G可能存在多個連通分支。測試

2.2 步驟二

  對有向無環圖G的每個起始頂點進行深度優先遍歷,最長的路徑的頂點數就是所求的單詞個數。圖1中(A,B,C,E,F,G) (I,B,C,E,F,G)和就是最長的路徑,頂點數爲6,因此單詞個數爲6。this

2.3 改進

  深度優先遍歷很是耗時,因此能夠在步驟一的過程當中記錄以頂點V爲結束點的最長有向線段的頂點數。當圖構建完成後,能夠對圖的全部頂點遍歷一次,找出V對應用的最大值就能夠了。spa

3 算法實現

3.1 方法一

import java.util.*;

/**
 * 解法一會生產超時

 * Declaration: All Rights Reserved !!!
 */
public class Main {
    /**
     * 有向圖
     */
    private static class G {
        // 頂點集合,經過頂點的名稱來找頂點。
        private final Map<String, V> VERTEX = new HashMap<>();
        // 有向無環圖的起始頂點,經過頂點的名稱來找起始頂點。
        private final Map<String, V> STARTING = new HashMap<>();
    }


    /**
     * 圖的頂點對象,使用圖的鄰接表表示
     */
    private static class V {
        // 頂點的名稱
        private String n;
        // 鄰接點
        private final Set<V> ADJ = new HashSet<>();

        V(String n) {
            this.n = n;
        }
    }

    public static void main(String[] args) {
//        Scanner scanner = new Scanner(System.in);
        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data2.txt"));
        while (scanner.hasNext()) {
            // 建立一個圖對象
            G g = new G();
            int n = scanner.nextInt();
            for (int i = 0; i < n; i++) {
                String a = scanner.next();
                String b = scanner.next();
                addEdge(g, a, b);
            }

            System.out.println(getLongestPathLength(g));
        }

        scanner.close();
    }

    /**
     * 求圖g最長路徑的長度
     * TIP: 這是一個很是耗時的方法
     *
     * @param g 圖
     * @return 最長路徑的長度
     */
    private static int getLongestPathLength(G g) {

        if (g == null || g.VERTEX.isEmpty()) {
            return 0;
        }

        int[] r = {0};
        int[] t = {0};
        Collection<V> vs = g.STARTING.values();
        for (V v : vs) {
            t[0] = 0;
            findLongestPathLength(v, t, r);
        }

        return r[0];
    }

    /**
     * 找以v頂點開始的最長路徑的長度
     *
     * @param v      頂點
     * @param curr   從最開始到當前處理的頂點的上一個頂點,一個有curr個頂點
     * @param result 長度爲1的數組,用於記錄結果,記錄最長路徑的頂點數
     */
    private static void findLongestPathLength(V v, int[] curr, int[] result) {
        curr[0]++;
        if (result[0] < curr[0]) {
            result[0] = curr[0];
        }

        Collection<V> vs = v.ADJ;
        // 處理領接點
        for (V t : vs) {
            findLongestPathLength(t, curr, result);
        }

        // 現場還原
        curr[0]--;
    }

    /**
     * 向圖g中添加邊(a, b);
     *
     * @param g 圖
     * @param a 邊的起始點
     * @param b 邊的終點
     */
    private static void addEdge(G g, String a, String b) {


        // 判斷兩個頂點是否都在圖中
        boolean ca = g.VERTEX.containsKey(a);
        boolean cb = g.VERTEX.containsKey(b);

        // 兩個頂點都已經存在了
        if (ca && cb) {
            // 將b設置爲a的鄰接點
            g.VERTEX.get(a).ADJ.add(g.VERTEX.get(b));
        }
        // 頂點a已經存在,b不存在
        else if (ca && !cb) {
            V bv = new V(b);
            // 將頂點b放到頂點集合中
            g.VERTEX.put(b, bv);
            // 將b設置爲a的鄰接點
            g.VERTEX.get(a).ADJ.add(bv);
        }
        // 頂點a不存存,b存在
        else if (!ca && cb) {
            V av = new V(a);
            // 將頂點a放到頂點集合中
            g.VERTEX.put(a, av);
            // 將b設置爲a的鄰接點
            av.ADJ.add(g.VERTEX.get(b));

            // 若是b起始頂點,加入(a, b)邊以後,b就不是起始頂點了
            if (g.STARTING.containsKey(b)) {
                g.STARTING.remove(b);
            }

            // a是新的起始頂點
            g.STARTING.put(a, av);

        }
        // 兩個頂點都不在圖中
        else {
            // 構造兩個頂點
            V av = new V(a);
            V bv = new V(b);
            // 將b設置爲a的鄰接點
            av.ADJ.add(bv);
            // 將頂點a、b放到頂點集合中
            g.VERTEX.put(a, av);
            g.VERTEX.put(b, bv);
            // a爲起始頂點
            g.STARTING.put(a, av);
        }
    }
}

 

3.1 方法二

import java.util.*;

/**
 * 解法二
 * Declaration: All Rights Reserved !!!
 */
public class Main2 {
    public static void main(String[] args) {
//        Scanner scanner = new Scanner(System.in);
        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data2.txt"));
        while (scanner.hasNext()) {
            int row = scanner.nextInt();

            // 頂點集合,同是記錄頂點爲終點的最長有向線段的頂點數
            // key(=String)爲起始頂點,value(=Integer)爲以key結點的最長有向線段的頂點數,當只有一個頂點時value爲1
            Map<String, Integer> vertex = new HashMap<>();
            // 圖
            // 記錄以key(=String)開爲起始頂點的有向邊,value(List<String>)鄰接頂點集合
            Map<String, List<String>> graph = new HashMap<>();

            for (int i = 0; i < row; i++) {
                // 輸入兩個單詞,同時也表示兩個頂表示的有向邊
                String a = scanner.next();
                String b = scanner.next();

                // 若是是新的頂點,就加入到頂點集合中
                if (!vertex.containsKey(a)) {
                    vertex.put(a, 1);
                }
                if (!vertex.containsKey(b)) {
                    vertex.put(b, 1);
                }

                // 獲取頂點a的有鄰接頂點集合,若是集合不存就建立
                List<String> list = graph.get(a);
                if (list == null) {
                    list = new ArrayList<>();
                    graph.put(a, list);
                }
                // 添加a的鄰接頂點b
                list.add(b);
                visitAll(a, b, vertex, graph);
            }

            int max = 0;
            for (Integer val : vertex.values()) {
                if (val > max) {
                    max = val;
                }
            }
            System.out.println(max);
        }

    }

    /**
     * 更新以b爲終點的最長有向線段的頂點數,其中(a, b)表示新添加的有向線段
     *
     * @param a      頂點
     * @param b      頂點
     * @param vertex 頂點集合
     * @param graph  有向圖
     */
    private static void visitAll(String a, String b, Map<String, Integer> vertex, Map<String, List<String>> graph) {
        // 以b爲終點的最長線段包含的頂點數
        int val = vertex.get(b);
        // 原先以a爲終點的最長線段包含的頂點數,再加上1,表示從包含(a, b),
        // 以b爲終點的最長線段包含的頂點數
        int t = vertex.get(a) + 1;
        // 記錄以b爲終點的最長有向線段的頂點數
        if (val < t) {
            vertex.put(b, t);

            // 接在b後面的頂點都要進行更新
            List<String> list = graph.get(b);
            if (list != null) {
                for (String s : list) {
                    visitAll(b, s, vertex, graph);
                }
            }
        }
        // 以b爲終點的最長有向線段的頂點數沒有發生變化,就不須要再進行處理
    }
}

3.1 方法三

import java.util.*;

/**
 * 解法三:
 * 由於解法一(Main)會生產超時,如今對他進行改進
 * Declaration: All Rights Reserved !!!
 */
public class Main3 {
    /**
     * 有向圖
     */
    private static class G {
        // 頂點集合,經過頂點的名稱來找頂點。
        private final Map<String, V> VERTEX = new HashMap<>();
    }


    /**
     * 圖的頂點對象,使用圖的鄰接表表示
     */
    private static class V {
        // 頂點的名稱
        private String n;
        // 以當前頂點爲終點的最長有向線段的頂點數,只有一個頂點時爲1
        private int v;
        // 鄰接點
        private final Set<V> ADJ = new HashSet<>();

        V(String n, int v) {
            this.n = n;
            this.v = v;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main3.class.getClassLoader().getResourceAsStream("data2.txt"));
        // 建立一個圖對象,能夠重複使用
        G g = new G();
        while (scanner.hasNext()) {
            // 清空內容
            g.VERTEX.clear();

            int n = scanner.nextInt();
            for (int i = 0; i < n; i++) {
                String a = scanner.next();
                String b = scanner.next();
                addEdge(g, a, b);
            }

            System.out.println(getLongestPathLength(g));
        }

        scanner.close();
    }

    /**
     * 求圖g最長路徑的長度
     *
     * @param g 圖
     * @return 最長路徑的長度
     */
    private static int getLongestPathLength(G g) {

        if (g == null || g.VERTEX.isEmpty()) {
            return 0;
        }

        int max = 0;
        Collection<V> vs = g.VERTEX.values();
        for (V v : vs) {
            if (max < v.v) {
                max = v.v;
            }
        }

        return max;
    }

    /**
     * 向圖g中添加邊(a, b);
     *
     * @param g 圖
     * @param a 邊的起始點
     * @param b 邊的終點
     */
    private static void addEdge(G g, String a, String b) {


//        // 判斷兩個頂點是否都在圖中
//        boolean ca = g.VERTEX.containsKey(a);
//        boolean cb = g.VERTEX.containsKey(b);

        V av = g.VERTEX.get(a);
        V bv = g.VERTEX.get(b);

        if (av == null) {
            av = new V(a, 1);
            // 將頂點a放到頂點集合中
            g.VERTEX.put(a, av);
        }

        if (bv == null) {
            bv = new V(b, 1);
            // 將頂點b放到頂點集合中
            g.VERTEX.put(b, bv);
        }

        // 將b設置爲a的鄰接點
        g.VERTEX.get(a).ADJ.add(g.VERTEX.get(b));
        update(g.VERTEX.get(a), g.VERTEX.get(b), g);

    }

    /**
     * 更新結束頂點的長度記數
     *
     * @param a 頂點
     * @param b 頂點
     * @param g 圖
     */
    private static void update(V a, V b, G g) {
        // 原先以a爲終點的最長線段包含的頂點數,再加上1,表示從包含(a, b),
        // 以b爲終點的最長線段包含的頂點數
        int lenA = a.v + 1;
        // 以b爲終點的最長線段包含的頂點數
        int lenB = b.v;

        if (lenA > lenB) {
            b.v = lenA;

            Set<V> vs = b.ADJ;
            for (V v : vs) {
                update(b, v, g);
            }
        }
    }
}

 

4 測試結果

這裏寫圖片描述

相關文章
相關標籤/搜索