一道Google的面试题 正则表达式解析器

开发 后端
解法:解析给定的表达式。生成对应的 NFA(Nondeterministic Finite Automation)。NFA转化为DFA(Deterministic Finite Automation)。利用DFA判断输入字符串。

一、题目如下:

--------------------------

Write a parser for a simplified regular expression

On an alphabet set [a-z], a simplified regular expression is much simpler than the normal regular expression.

It has only two meta characters: '.' and '*'.

'.' -- exact one arbitrary character match.

'*' -- zero or more arbitrary character match.

--------------------------

举个例子,表达式 ab.*d 能匹配 'abcd', 'abccd', 'abad'。 不能匹配'abc', ' '(空字符串), 'abd'。

二、解法:

解析给定的表达式

生成对应的 NFA(Nondeterministic Finite Automation)

NFA转化为DFA(Deterministic Finite Automation)

利用DFA判断输入字符串

不懂正则表达式? 参考http://deerchao.net/tutorials/regex/regex.htm

不懂NFA? 参考http://planning.cs.uiuc.edu/node558.html

不懂DFA?参考http://lambda.uta.edu/cse5317/notes/node8.html

三、相关代码:

FiniteAutomata.java 和State.java,随手写写,请选择性参考。

FiniteAutomata.java

  1. package parser;  
  2.  
  3. import java.util.HashMap;  
  4. import java.util.HashSet;  
  5. import java.util.Iterator;  
  6. import java.util.LinkedList;  
  7. import java.util.Map;  
  8. import java.util.Map.Entry;  
  9. import java.util.Queue;  
  10. import java.util.Set;  
  11.  
  12. public class FiniteAutomata {  
  13.  
  14.     private State start;  
  15.  
  16.     private final static char[] inputs;  
  17.  
  18.     static {  
  19.         inputs = new char[26];  
  20.         for (char i = 0; i < 26; i++) {  
  21.             inputs[i] = (char) ('a' + i);  
  22.         }  
  23.     }  
  24.  
  25.     private final char episilon = 0;  
  26.  
  27.     private char stateId;  
  28.  
  29.     public void compile(String pattern) {  
  30.         this.stateId = 'A';  
  31.         State NFA = toNFA(pattern);  
  32.         this.start = convertToDFA(NFA);  
  33.     }  
  34.  
  35.     private State convertToDFA(State NFA) {  
  36.         Map<Set<State>, State> cache = new HashMap<Set<State>, State>();  
  37.         Queue<Set<State>> queue = new LinkedList<Set<State>>();  
  38.  
  39.         // start state of DFA  
  40.         Set<State> start = episilon(NFA);  
  41.         State starter = nextState();  
  42.         cache.put(start, starter);  
  43.         queue.offer(start);  
  44.         while (!queue.isEmpty()) {  
  45.             Set<State> currentEpisilon = queue.poll();  
  46.             State current = cache.get(currentEpisilon);  
  47.             // create new State  
  48.             for (char input : inputs) {  
  49.                 Set<State> nextEpisilon = move(currentEpisilon, input);  
  50.                 if (nextEpisilon.isEmpty()) {  
  51.                     continue;  
  52.                 }  
  53.                 State next;  
  54.                 if (!cache.containsKey(nextEpisilon)) {  
  55.                     next = nextState();  
  56.                     cache.put(nextEpisilon, next);  
  57.                     queue.offer(nextEpisilon);  
  58.                 } else {  
  59.                     next = cache.get(nextEpisilon);  
  60.                 }  
  61.                 boolean isAccept = containsAcceptState(nextEpisilon);  
  62.                 next.setAccept(isAccept);  
  63.                 current.setTransition(input, next);  
  64.             }  
  65.         }  
  66.         return starter;  
  67.     }  
  68.  
  69.     private Set<State> move(Set<State> currentEpisilon, char input) {  
  70.         Set<State> result = new HashSet<State>();  
  71.         for (State s : currentEpisilon) {  
  72.             State next = s.transit(input);  
  73.             if (next != null) {  
  74.                 result.add(next);  
  75.             }  
  76.         }  
  77.         return episilon(result);  
  78.     }  
  79.  
  80.     private boolean containsAcceptState(Set<State> nextEpisilon) {  
  81.         for (State state : nextEpisilon) {  
  82.             if (state.isAccept()) {  
  83.                 return true;  
  84.             }  
  85.         }  
  86.         return false;  
  87.     }  
  88.  
  89.     private Set<State> episilon(State s) {  
  90.         Set<State> cache = new HashSet<State>();  
  91.         cache.add(s);  
  92.         return episilon(s, cache);  
  93.     }  
  94.  
  95.     private Set<State> episilon(Set<State> states) {  
  96.         Set<State> cache = new HashSet<State>();  
  97.         for (State s : states) {  
  98.             cache.add(s);  
  99.             cache.addAll(episilon(s, cache));  
  100.         }  
  101.         return cache;  
  102.     }  
  103.  
  104.     private Set<State> episilon(State s, Set<State> cache) {  
  105.         State next = s.transit(episilon);  
  106.         if (next != null && !cache.contains(next)) {  
  107.             cache.add(next);  
  108.             cache.addAll(episilon(s, cache));  
  109.         }  
  110.         return cache;  
  111.     }  
  112.  
  113.     private State toNFA(String pattern) {  
  114.         char[] expr = pattern.toCharArray();  
  115.         State currentState = nextState();  
  116.         State starter = currentState;  
  117.         for (char c : expr) {  
  118.             if (c == '.') {  
  119.                 State nextState = nextState();  
  120.                 // add each transition for all inputs  
  121.                 for (char i : inputs) {  
  122.                     currentState.setTransition(i, nextState);  
  123.                 }  
  124.                 currentState = nextState;  
  125.             } else if (c == '*') {  
  126.                 State nextState = nextState();  
  127.                 // add each transition for all inputs  
  128.                 for (char i : inputs) {  
  129.                     currentState.setTransition(i, nextState);  
  130.                 }  
  131.                 currentState.setTransition(episilon, nextState);  
  132.                 nextState.setTransition(episilon, currentState);  
  133.                 currentState = nextState;  
  134.             } else if (c >= 'a' && c <= 'z') {  
  135.                 State nextState = nextState();  
  136.                 currentState.setTransition(c, nextState);  
  137.                 currentState = nextState;  
  138.             } else {  
  139.                 throw new RuntimeException("Unrecognized expression");  
  140.             }  
  141.         }  
  142.         currentState.setAccept(true);  
  143.         return starter;  
  144.     }  
  145.  
  146.     private State nextState() {  
  147.         return new State(stateId++);  
  148.     }  
  149.  
  150.     public void constructDFA(Map<State, Map<Character, State>> mapping) {  
  151.         Iterator<Map.Entry<State, Map<Character, State>>> it = mapping  
  152.                 .entrySet().iterator();  
  153.         while (it.hasNext()) {  
  154.             Entry<State, Map<Character, State>> entry = it.next();  
  155.             State state = entry.getKey();  
  156.             Map<Character, State> transition = entry.getValue();  
  157.             state.setTransition(transition);  
  158.         }  
  159.     }  
  160.  
  161.     public boolean match(String c) {  
  162.         char[] candidate = c.toCharArray();  
  163.         for (char d : candidate) {  
  164.             start = start.transit(d);  
  165.             if (start == null) {  
  166.                 return false;  
  167.             }  
  168.         }  
  169.         return start.isAccept();  
  170.     }  
  171.  
  172.     public static String[] patterns = { "abc""*""*abc""*abc""a*bc",  
  173.             "a*bc""a*""a*""a*""a*""*abc*""*****""..."".*",  
  174.             ".bc*"".b*c*a""*""abc""*a""a"".a*c""a.*b""..""",  
  175.             "" };  
  176.  
  177.     public static String[] candidates = { "abc""abc""abc""aaabbbabc",  
  178.             "aaabbbabc""abc""abc""a""aa""abcdef""abc""abc",  
  179.             "abc""abc""abc""abca""""abcd""abcd""""abc""abc",  
  180.             "abc""""abc" };  
  181.  
  182.     public static void main(String[] args) {  
  183.         FiniteAutomata fa = new FiniteAutomata();  
  184.         for (int i = 0; i < patterns.length; i++) {  
  185.             fa.compile(patterns[i]);  
  186.             System.out.println(fa.match(candidates[i]));  
  187.         }  
  188.     }  
  189.  

State.java

  1. package parser;  
  2.  
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.  
  6. public class State {  
  7.  
  8.     private boolean accept = false;  
  9.  
  10.     private char id;  
  11.  
  12.     private Map<Character, State> mapping = new HashMap<Character, State>();  
  13.  
  14.     public State(char c) {  
  15.         this.id = c;  
  16.     }  
  17.  
  18.     public State(char c, boolean isAccept) {  
  19.         this.id = c;  
  20.         this.accept = isAccept;  
  21.     }  
  22.  
  23.     public State transit(char input) {  
  24.         return mapping.get(input);  
  25.     }  
  26.  
  27.     public void setTransition(char input, State next) {  
  28.         this.mapping.put(input, next);  
  29.     }  
  30.  
  31.     public void setTransition(Map<Character, State> mapping) {  
  32.         this.mapping = mapping;  
  33.     }  
  34.  
  35.     public boolean isAccept() {  
  36.         return this.accept;  
  37.     }  
  38.  
  39.     public void setAccept(boolean b) {  
  40.         this.accept = b;  
  41.     }  
  42.  
  43.     @Override 
  44.     public int hashCode() {  
  45.         final int prime = 31;  
  46.         int result = 1;  
  47.         result = prime * result + id;  
  48.         return result;  
  49.     }  
  50.  
  51.     @Override 
  52.     public boolean equals(Object obj) {  
  53.         if (this == obj)  
  54.             return true;  
  55.         if (obj == null)  
  56.             return false;  
  57.         if (getClass() != obj.getClass())  
  58.             return false;  
  59.         State other = (State) obj;  
  60.         if (id != other.id)  
  61.             return false;  
  62.         return true;  
  63.     }  
  64.  
  65.     @Override 
  66.     public String toString() {  
  67.         return "State [id=" + id + "]";  
  68.     }  
  69.       

 原文链接:http://daniel-wuz.iteye.com/blog/1608376

责任编辑:张伟 来源: daniel.wuz的博客
相关推荐

2010-07-14 09:37:46

Perl正则表达式

2011-06-16 15:28:31

正则表达式

2010-08-09 13:58:59

Flex正则表达式

2010-07-13 17:03:53

Perl正则表达式

2010-07-28 11:06:41

Flex正则表达式

2018-09-27 15:25:08

正则表达式前端

2022-01-25 09:05:00

Python字符串网络爬虫

2009-09-16 10:59:24

PHP正则表达式元字符

2020-09-04 09:16:04

Python正则表达式虚拟机

2010-07-14 10:06:55

Perl正则表达式

2009-09-16 18:08:14

正则表达式匹配单词

2011-05-23 11:27:32

面试题面试java

2018-03-06 15:30:47

Java面试题

2009-09-16 16:48:03

正则表达式匹配数字

2010-03-25 18:25:36

Python正则表达式

2009-09-16 12:29:27

PHP正则表达式正则表达式修饰符

2009-09-16 18:19:34

正则表达式组

2021-01-27 11:34:19

Python正则表达式字符串

2017-05-12 10:47:45

Linux正则表达式程序基础

2009-02-18 09:48:20

正则表达式Java教程
点赞
收藏

51CTO技术栈公众号