Advertisement
jsprieto10

Shortest Superstring problem, with graph, KMP and Hamiltonia

Dec 9th, 2017
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 13.57 KB | None | 0 0
  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6.  
  7. /**
  8.  * Este algortimo esta basado en el trabajo de Tesis, de katarina Martinova de la universidad
  9.  * Masarykova, llamado Algorithms for shortest superstring problems de 2014.
  10.  *
  11.  *
  12.  * @author js.prieto10
  13.  */
  14. public class ProblemaC {
  15.  
  16.     private List<String> strings;
  17.     private String superstring;
  18.     private List<String> nodes;
  19.     private int totalLength;
  20.  
  21.     /**
  22.      * constructor
  23.      *
  24.      * @param words set de palabras
  25.      * @param tamaño total de todas las palabras del set
  26.      */
  27.     public ProblemaC(List<String> words, int totalLength) {
  28.         strings = words;
  29.         this.totalLength = totalLength;
  30.         nodes = new ArrayList<>();
  31.     }
  32.  
  33.  
  34.     public String getSuperstring() {
  35.         return superstring;
  36.     }
  37.    
  38.  
  39.     /**
  40.      * Generación de arcos con pesos a partir Knuth-Morris-Pratt string matching algorithm
  41.      *
  42.      * @param List, la lista de nodos del grafo
  43.      * @return arcos del grafo
  44.      */
  45.     protected List<Edge> countWeights(List<String> nodes) {
  46.        
  47.         List<Edge> edges = new ArrayList<>();
  48.         String textString;
  49.         String patternString;
  50.         int l = 0;
  51.  
  52.         int state;
  53.         String overlap;
  54.  
  55.         for (int pattern = 1; pattern < nodes.size(); pattern++) {              
  56.             patternString = nodes.get(pattern);
  57.  
  58.             for (int text = 0; text < nodes.size() - 1; text++) {  
  59.                 if (text == pattern) {
  60.                     continue;
  61.                 }
  62.                 if (text == 0 || pattern == nodes.size() - 1) {
  63.                     edges.add(new Edge(text, pattern, 0, Edge.Status.FREE));
  64.                     continue;
  65.                 }
  66.                 textString = nodes.get(text);
  67.                 if ((l = textString.length()) > patternString.length()) {
  68.                     textString = textString.substring(l - patternString.length(), l);
  69.                 }
  70.                 state = patternString.length();
  71.                 overlap = patternString;
  72.                 while (true) {
  73.                     if (state == 0) {
  74.                         break;
  75.                     }
  76.                     if (textString.endsWith(overlap)) {
  77.                         break;
  78.                     } else {
  79.                         state--;
  80.                         overlap = overlap.substring(0, state);
  81.                     }
  82.                 }
  83.                 edges.add(new Edge(text, pattern, state, Edge.Status.FREE));
  84.             }
  85.         }
  86.         return edges;
  87.     }
  88.  
  89.     /**
  90.      * inserta los nodos en un "bucket" de manera descendente
  91.      *
  92.      * @param list, (bucket)
  93.      * @param e edge to be inserted
  94.      */
  95.     protected void decInsertionSort(List<Edge> list, Edge e) {
  96.         if (list.isEmpty()) {
  97.             list.add(e);
  98.             return;
  99.         }
  100.         for (int i = 0; i < list.size(); i++) {
  101.             if (e.getWeight() >= list.get(i).getWeight()) {
  102.                 list.add(i, e);
  103.                 break;
  104.             }
  105.             if (i == list.size() - 1) {
  106.                 list.add(e);
  107.             }
  108.         }
  109.     }
  110.  
  111.     /**
  112.      * splits edges into buckets, buckets are sortet in decreasing order buckets
  113.      * are concatenated in opposite order into resulting sorted list of edges
  114.      *
  115.      * @param edges, lista de arcos
  116.      * @param maxValue, tamaño total de todos lo strings de la lista
  117.      * @return sorted list of edges
  118.      */
  119.     protected List<Edge> bucketSort(List<Edge> edges, int maxValue) {
  120.         int number;
  121.         number = edges.size();
  122.         int zeros;
  123.         zeros = 2 * strings.size() + 1;
  124.         int bucketNum;
  125.         bucketNum = number / zeros;
  126.         if (number % zeros > 0) {
  127.             bucketNum++;
  128.         }
  129.         int range;
  130.         if (bucketNum == 1) {
  131.             range = maxValue;
  132.         } else {
  133.             range = maxValue / (bucketNum - 1);
  134.         }
  135.         List<Edge>[] buckets;
  136.         buckets = new List[bucketNum];
  137.         for (int i = 0; i < bucketNum; i++) {
  138.             buckets[i] = new ArrayList<>();
  139.         }
  140.         int limit;
  141.         for (Edge e : edges) {
  142.             if (e.getWeight() == 0) {
  143.                 buckets[0].add(e);
  144.                 continue;
  145.             }
  146.             limit = 0;
  147.             for (int i = 1; i < bucketNum; i++) {
  148.                 if (i == bucketNum - 1) {
  149.                     limit = maxValue;
  150.                 } else {
  151.                     limit += range;
  152.                 }
  153.                 if (e.getWeight() <= limit) {
  154.                     decInsertionSort(buckets[i], e);
  155.                     break;
  156.                 }
  157.             }
  158.         }
  159.  
  160.         List<Edge> orderedEdges = new ArrayList<>();
  161.         for (int i = bucketNum - 1; i > -1; i--) {
  162.             orderedEdges.addAll(buckets[i]);
  163.         }
  164.         return orderedEdges;
  165.     }
  166.  
  167.     /**
  168.      * conecta las arcos del camino que pueden ser conectados
  169.      *
  170.      * @param path, camino hamiltoiano
  171.      */
  172.     protected void organizePath(List<Edge> path) {
  173.         if (path.size() <= 1) {
  174.             return;
  175.         }
  176.         List<Integer> starts = new ArrayList<>();
  177.         starts.add(0);
  178.         List<Integer> ends = new ArrayList<>();
  179.         for (int i = 0; i < path.size(); i++) {
  180.             if (i == path.size() - 1) {
  181.                 ends.add(i);
  182.                 break;
  183.             }
  184.             if (path.get(i).getEnd() != path.get(i + 1).getStart()) {
  185.                 ends.add(i);
  186.                 starts.add(i + 1);
  187.             }
  188.         }
  189.         if (starts.size() == 1) {
  190.             return;
  191.         }
  192.         for (int i = 0; i < starts.size(); i++) {
  193.             for (int j = 0; j < ends.size(); j++) {
  194.                 if (i == j) {
  195.                     continue;
  196.                 }
  197.                 if (path.get(starts.get(i)).getStart() == path.get(ends.get(j)).getEnd()) {
  198.                     if (ends.get(j) < starts.get(i)) {                    
  199.                         List<Edge> selection = path.subList(starts.get(i), ends.get(i) + 1);
  200.                         int length = selection.size();                      
  201.                         path.addAll(ends.get(j) + 1, selection);
  202.                         path.subList(starts.get(i) + length, ends.get(i) + 1 + length).clear();                
  203.                         return;
  204.                     } else {                    
  205.                         List<Edge> selection = path.subList(starts.get(j), ends.get(j) + 1);
  206.                         int length = selection.size();                    
  207.                         path.addAll(starts.get(i), selection);
  208.                         path.subList(starts.get(j) + length, ends.get(j) + 1 + length).clear();
  209.                         return;
  210.                     }
  211.                 }
  212.             }
  213.         }
  214.     }
  215.  
  216.     /**
  217.      * inserta un nuevo arco al caminimo hamiltoiano
  218.      *
  219.      * @param path, camino hamiltoniano parcial
  220.      * @param e, arco ha ser insertado
  221.      */
  222.     protected void addToHamPath(List<Edge> path, Edge e) {
  223.         if (path.isEmpty()) {
  224.             path.add(e);
  225.             return;
  226.         }
  227.         if (e.getStart() == 0) {
  228.             path.add(0, e);
  229.             return;
  230.         }
  231.  
  232.         for (int i = 0; i < path.size(); i++) {
  233.             if (path.get(i).getEnd() == e.getStart()) {
  234.                 path.add(i + 1, e);
  235.                 return;
  236.             }
  237.             if (e.getEnd() == path.get(i).getStart()) {
  238.                 path.add(i, e);
  239.                 return;
  240.             }
  241.         }
  242.         path.add(e);
  243.     }
  244.  
  245.     /**
  246.      *
  247.      *
  248.      * @param path, camino hamiltoniano parcial
  249.      * @param e, ultimo arco insertado
  250.      * @return el valor final parcial
  251.      */
  252.     protected int getPartEnd(List<Edge> path, Edge e) {
  253.         if (path.size() == 1) {
  254.             return e.getEnd();
  255.         }
  256.         int index = path.indexOf(e);
  257.         int end = e.getEnd();
  258.         for (int i = index; i < path.size(); i++) {
  259.             if (i == path.size() - 1) {
  260.                 return path.get(path.size() - 1).getEnd();
  261.             }
  262.             if ((end = path.get(i).getEnd()) != path.get(i + 1).getStart()) {
  263.                 break;
  264.             }
  265.         }
  266.         return end;
  267.     }
  268.  
  269.     /**
  270.      *
  271.      * @param path, camino hamiltiano parcial
  272.      * @param e, ultimo nodo en ser insertado
  273.      * @return el valor inicial del camino
  274.      */
  275.     protected int getPartStart(List<Edge> path, Edge e) {  
  276.         if (path.size() == 1) {
  277.             return e.getStart();
  278.         }
  279.         int index = path.indexOf(e);
  280.         int start = e.getStart();
  281.         for (int i = index; i > -1; i--) {
  282.             if (i == 0) {
  283.                 return path.get(0).getStart();
  284.             }
  285.             if ((start = path.get(i).getStart()) != path.get(i - 1).getEnd()) {
  286.                 break;
  287.             }
  288.         }
  289.         return start;
  290.     }
  291.  
  292.     /**
  293.      * algoritmo para hallar el camino hamiltiano
  294.      *
  295.      * @param edges, lista de arcos
  296.      * @return El camino hamiltiano más largo
  297.      */
  298.     protected List<Edge> hamPath(List<Edge> edges) {
  299.         List<Edge> path = new ArrayList<>();
  300.         int partStart = -1;
  301.         int partEnd = -1;
  302.         for (Edge edge : edges) {
  303.             if (!edge.getStatus().equals(Edge.Status.FREE)) {
  304.                 continue;
  305.             }
  306.             edge.setStatus(Edge.Status.NOT_FREE);
  307.             addToHamPath(path, edge);
  308.             organizePath(path);
  309.            
  310.             partStart = getPartStart(path, edge);
  311.             partEnd = getPartEnd(path, edge);
  312.  
  313.             for (Edge e : edges) {
  314.                 if (e.getStart() == partEnd && e.getEnd() == partStart) {
  315.                     e.setStatus(Edge.Status.MAKING_CIRCLE);
  316.                     continue;
  317.                 }
  318.                 if (e.getStart() == edge.getEnd() && e.getEnd() == edge.getStart()) {
  319.                     e.setStatus(Edge.Status.MAKING_CIRCLE);
  320.                     continue;
  321.                 }
  322.                 if (edge.getStart() == e.getStart()) {
  323.                     e.setStatus(Edge.Status.NOT_FREE);
  324.                     continue;
  325.                 }
  326.                 if (edge.getEnd() == e.getEnd()) {
  327.                     e.setStatus(Edge.Status.NOT_FREE);
  328.                 }
  329.  
  330.             }
  331.         }
  332.         return path;
  333.     }
  334.  
  335.     /**
  336.      * Crea un superstring a partir del camino hamiltiano generado
  337.      *
  338.      * @param path, camino hamiltiano
  339.      * @return superstring
  340.      */
  341.     protected String buildSuperstring(List<Edge> path) {
  342.         String string = nodes.get(path.get(0).getStart());
  343.         for (int i = 0; i < path.size(); i++) {
  344.             string = string + nodes.get(path.get(i).getEnd()).substring(path.get(i).getWeight());
  345.         }
  346.         return string;
  347.     }
  348.    
  349.     public void run() {
  350.         List<Edge> edges;
  351.         List<Edge> path;  
  352.  
  353.         nodes.add(""); // añadir un nodo inicial
  354.         nodes.addAll(strings); // añada toda la lista de nodos
  355.         nodes.add(""); // añade un nodo final
  356.         edges = countWeights(nodes); // crea los arcos con peso dado al algoritmo KMP string matching
  357.         edges = bucketSort(edges, totalLength); // ordena los arcos de manera decreciente        
  358.         path = hamPath(edges); // genera el camino hamiltiano
  359.         superstring = buildSuperstring(path); // crea un superstring a partir del camino hamiltoniano.
  360.        
  361.     }
  362.  
  363.     /**
  364.      *  Clase arco, para la creación de un grafo dirigido con peso.
  365.      */
  366.     public static class Edge {
  367.  
  368.         public enum Status {FREE, NOT_FREE, MAKING_CIRCLE}
  369.  
  370.         private int start;
  371.         private int end;
  372.         private int weight;
  373.         private Status status;
  374.  
  375.         public Edge(int start, int end, int weight, Status status) {
  376.             this.start = start;
  377.             this.end = end;
  378.             this.weight = weight;
  379.             this.status = status;
  380.         }
  381.  
  382.         public int getStart() {
  383.             return start;
  384.         }
  385.  
  386.         public void setStart(int start) {
  387.             this.start = start;
  388.         }
  389.  
  390.         public int getEnd() {
  391.             return end;
  392.         }
  393.  
  394.         public void setEnd(int end) {
  395.             this.end = end;
  396.         }
  397.  
  398.         public int getWeight() {
  399.             return weight;
  400.         }
  401.  
  402.         public void setWeight(int weight) {
  403.             this.weight = weight;
  404.         }
  405.  
  406.         public Status getStatus() {
  407.             return status;
  408.         }
  409.  
  410.         public void setStatus(Status status) {
  411.             this.status = status;
  412.         }
  413.  
  414.         @Override
  415.         public String toString() {
  416.             return "[" + start + ", " + end + ", " + weight + ", " + status + "]";
  417.         }
  418.  
  419.     }
  420.    
  421.     public static void main(String arg[]) throws IOException {
  422.  
  423.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  424.         String lineain;
  425.         String data[];
  426.         List<String> fragments;
  427.         ProblemaC superStringCreator;
  428.         int n;
  429.         int k;
  430.        
  431.         while (true){
  432.             fragments = new ArrayList<String>();
  433.             lineain = br.readLine();
  434.             if (lineain.isEmpty()) return;
  435.             data = lineain.split(" ");
  436.             n = Integer.parseInt(data[0]);
  437.             k = Integer.parseInt(data[1]);
  438.             for (int i = 0; i != n; i++) {
  439.                 lineain = br.readLine();
  440.                 fragments.remove(lineain);
  441.                 fragments.add(lineain);
  442.  
  443.             }
  444.             superStringCreator = new ProblemaC(fragments, k);
  445.             superStringCreator.run();
  446.             System.out.println(superStringCreator.getSuperstring() );
  447.         }
  448.  
  449.     }
  450.    
  451. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement