lihongjie0209 / myblog

4 stars 0 forks source link

NFA Java 实现 #189

Open lihongjie0209 opened 4 years ago

lihongjie0209 commented 4 years ago

import java.util.*;

/**
 * Nondeterministic finite automata example
 *
 * Specify states as an array of their names, with ",S" (start) after one of them and ",E" (end) after at least one of them
 * Specify transitions as an array of "from,to,label" for a unique label or "from,to,label1,label2,..." for more
 *
 * Then try matching a string
 *
 * @author Chris Bailey-Kellogg, Dartmouth CS 10, Winter 2014
 * @author inspired by a CS 8 problem set by Scot Drysdale (more beautiful in Haskell, of course)
 * @author CBK, small revisions, Spring 2016
 *
 */
public class NFA {
    String start;
    Set<String> ends;
    Map<String, Map<Character,List<String>>> transitions; // state -> (character -> [next states])
    // note the difference from DFA: can have multiple different transitions from state for character

    /**
     * Constructs the DFA from the arrays, as specified in the overall header
     */
    NFA(String[] ss, String[] ts) {
        ends = new TreeSet<String>();
        transitions = new TreeMap<String, Map<Character,List<String>>>();

        // States
        for (String v : ss) {
            String[] pieces = v.split(",");
            if (pieces.length>1) {
                if (pieces[1].equals("S")) start = pieces[0];
                else if (pieces[1].equals("E")) ends.add(pieces[0]);
            }
        }

        // Transitions
        for (String e : ts) {
            String[] pieces = e.split(",");
            String from = pieces[0], to = pieces[1];
            if (!transitions.containsKey(from)) transitions.put(from, new TreeMap<Character,List<String>>());
            for (int i=2; i<pieces.length; i++) {
                char c = pieces[i].charAt(0);
                // difference from DFA: list of next states
                if (!transitions.get(from).containsKey(c)) transitions.get(from).put(c, new ArrayList<String>());
                transitions.get(from).get(c).add(to);
            }
        }

        System.out.println("start:"+start);
        System.out.println("end:"+ends);
        System.out.println("transitions:"+transitions);
    }

    /**
     * Returns whether or not the DFA accepts the string --
     * follows transitions according to its characters, landing in an end state at the end of the string
     */
    public boolean match(String s) {
        // difference from DFA: multiple current states
        Set<String> currStates = new TreeSet<String>();
        currStates.add(start);
        for (int i=0; i<s.length(); i++) {
            char c = s.charAt(i);
            Set<String> nextStates = new TreeSet<String>();
            // transition from each current state to each of its next states
            for (String state : currStates)
                if (transitions.get(state).containsKey(c))
                    nextStates.addAll(transitions.get(state).get(c));
            if (nextStates.isEmpty()) return false; // no way forward for this input
            currStates = nextStates;
        }
        // end up in multiple states -- accept if any is an end state
        for (String state : currStates) {
            if (ends.contains(state)) return true;
        }
        return false;
    }

    /**
     * Helper method to test matching against a bunch of strings, printing the results
     */
    public void test(String name, String[] inputs) {
        System.out.println("***" + name);
        for (String s : inputs)
            System.out.println(s + ":" + match(s));
    }

    public static void main(String[] args) {
        String[] ss1 = { "A,S", "B,E", "C" };
        String[] ts1 = { "A,B,0", "A,C,1", "B,B,0,1", "C,C,0,1" };
        NFA dfa1 = new NFA(ss1, ts1);

        String[] testsT1 = { "0", "00", "00000", "0010101" };
        dfa1.test("testsT1", testsT1);
        String[] testsF1 = { "", "1", "1100110" };
        dfa1.test("testsF1", testsF1);

        String[] ss1b = { "A,S", "B,E" };
        String[] ts1b = { "A,B,0", "B,B,0,1" };
        NFA nfa1 = new NFA(ss1b, ts1b);

        nfa1.test("testsT1b", testsT1);
        nfa1.test("testsF1b", testsF1);

        String[] ss2 = { "A,S", "B", "C", "D,E", "E,E" };
        String[] ts2 = { "A,A,0,1", "A,B,1", "B,D,1", "D,D,0,1", "A,C,0", "C,E,0", "E,E,0,1" };
        NFA nfa2 = new NFA(ss2, ts2);

        String[] testsT2 = { "00", "001", "0001100", "010110101", "101010100" };
        nfa2.test("testsT2", testsT2);
        String[] testsF2 = { "0", "010", "0101010", "1", "101010101010101010101" };
        nfa2.test("testsF2", testsF2);
    }
}