请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但是与”aa.a”和”ab*a”均不匹配
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
/**
* @author huangeryu
* 实现一个函数用来匹配包括'.'和'*'的正则表达式,模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)
* 思路:利用模式建立不确定性有穷自动机(NFA),再把NFA转换为确定性有穷自动机(DFA)
* */
public class Solution
{
private char lookahead;
private char current;
private static int indexIterator=0;
public boolean match(char[] str, char[] pattern)
{
if(pattern!=null && pattern.length!=0)current=pattern[0];
//构建NFA
NFA nfa=new NFA();
for(int i=1;i<=pattern.length;i++)
{
lookahead=(i==pattern.length)?' ':pattern[i];
nfa.construct(lookahead, current);
current=lookahead;
}
//转换为DFA
DFA dfa=new DFA(nfa);
dfa.construct();
return dfa.matchDfa(str);
}
/**
* @param alphabet 输入条件
* @param next 满足输入条件的下一个节点
* */
class Director
{
private char alphabet;
private Node next;
public Director(char alphabet,Node next)
{
this.alphabet=alphabet;
this.next=next;
}
}
/**
* @param index 节点的名称
* @param lists 节点对应的出度边@see Director
* @param isEnd 表示节点是否是接受节点,如果是,为true,否则为false
* */
class Node
{
private final int index;
private ArrayList<Director> lists;
private boolean isEnd;
public Node(int index)
{
this.index=index;
lists=new ArrayList<Director>();
isEnd=false;
}
public int getIndex()
{
return this.index;
}
@Override
public int hashCode()
{
return this.index;
}
@Override
public boolean equals(Object node)
{
if(node instanceof Node)
{
Node temp=(Node)node;
if(temp.index==this.index)return true;
}
return false;
}
}
/**
* @param head 确定有穷自动机的头节点{@link Node}
* @param nfa 不确定性有穷自动机{@link NFA}
* */
class DFA
{
private Node head;
private NFA nfa;
public DFA(NFA nfa)
{
this.nfa=nfa;
}
/**
* 利用不确定性有穷自动机{@link NFA}构造确定性有穷自动机
* */
public void construct()
{
Deque<Set<Node>> queue=new ArrayDeque<Set<Node>>();
Set<Node> start=new HashSet<Node>();
Map<Set<Node>,Node> map=new HashMap<>();
start.add(nfa.head);
Boolean[] bln={false};
Set<Node> s=nfa.closure_alpha(start, (char)0,bln);
this.head=new Node(indexIterator++);
this.head.isEnd=bln[0];
map.put(s, this.head);
queue.add(s);
while(!queue.isEmpty())
{
Set<Node> t=queue.poll();
Node p=map.get(t);
for(Character c:nfa.set)
{
bln[0]=false;
Set<Node> sn=nfa.closure_alpha(t, c.charValue(),bln);
if(sn.isEmpty())continue;
if(map.containsKey(sn))
{
p.lists.add(new Director(c.charValue(),map.get(sn)));
// System.out.println(p.index+"-->"+map.get(sn).index+" "+c);
}
else
{
Node node=new Node(indexIterator++);
node.isEnd=bln[0];
map.put(sn, node);
p.lists.add(new Director(c.charValue(),node));
// System.out.println(p.index+"-->"+node.index+" "+c+" "+bln[0]);
queue.offer(sn);
}
}
}
}
/**
* @param str 和模式进行匹配的字符串
* @return 如果匹配成功,返回true,否则,返回false
* */
public boolean matchDfa(char[] str)
{
Set<Node>result=new HashSet<Node>();
result.add(head);
Set<Node>temp=new HashSet<Node>();
for(int i=0;i<str.length;i++)
{
char c=str[i];
for(Node np:result)
{
for(Director d:np.lists)
{
if(d.alphabet==c || d.alphabet=='.')temp.add(d.next);
}
}
result.clear();
Set<Node>t=temp;
temp=result;
result=t;
}
for(Node n:result)
{
// System.out.print(n.index+" ");
if(n.isEnd)return true;
}
return false;
}
}
/**
* @param head 不确定性有穷自动机的头节点{@link Node}
* @param last 总是指向尾部节点,为计算方便而引进
* @param set 节点之间转移条件的集合{@link Director}
* */
class NFA
{
private Node head;
private Node last;
private HashSet<Character> set;
public NFA()
{
head=new Node(indexIterator++);
last=head;
set=new HashSet<Character>();
}
/**
* 利用两字符来识别正则法则中的s*模式和s.模式
* @param lookahead 向前看字符
* @param current 当前字符
* */
public void construct(char lookahead,char current)
{
if(current!='*')set.add(current);
if(lookahead==' ')
{
if(current=='*')return;
Node temp=new Node(indexIterator++);
temp.isEnd=true;
last.lists.add(new Director(current,temp));
// System.out.println(last.index+"-->"+temp.index+" "+current);
last=temp;
}
else if(lookahead=='*')
{
Node temp1=new Node(indexIterator++);
Node temp2=new Node(indexIterator++);
Node temp3=new Node(indexIterator++);
last.lists.add(new Director((char)0,temp1));
// System.out.println(last.index+"-->"+temp1.index+" "+0);
last.lists.add(new Director((char)0,temp2));
// System.out.println(last.index+"-->"+temp2.index+" "+0);
temp1.lists.add(new Director(current,temp3));
// System.out.println(temp1.index+"-->"+temp3.index+" "+current);
temp3.lists.add(new Director((char) 0,temp1));
// System.out.println(temp3.index+"-->"+temp1.index+" "+0);
temp3.lists.add(new Director((char)0,temp2));
// System.out.println(temp3.index+"-->"+temp2.index+" "+0);
last=temp2;
}
else if(current!='*')
{
Node temp1=new Node(indexIterator++);
last.lists.add(new Director(current,temp1));
// System.out.println(last.index+"-->"+temp1.index+" "+current);
last=temp1;
}
}
/**
* @param set 输入集合
* @param alpha 输入条件{@link NFA}
* @return 输入集合在输入条件下转移的闭包
* @param re 如果返回的闭包中包含终止节点,则re[0]为true,否则false {@link Node}
* */
public Set<Node> closure_alpha(Set<Node> set,char alpha,Boolean[] re)
{
Set<Node> reSet=new HashSet<Node>();
if(alpha!=0)
{
for(Node n:set)
{
for(Director d:n.lists)
{
if(d.alphabet==alpha)
{
reSet.add(d.next);
if(d.next.isEnd && re!=null)re[0]=true;
}
}
}
}
else
{
reSet.addAll(set);
}
Queue<Node> queue=new ArrayDeque<Node>(reSet);
while(!queue.isEmpty())
{
Node node=queue.poll();
for(Director d:node.lists)
{
if(d.alphabet==0)
{
reSet.add(d.next);
if(d.next.isEnd&& re!=null)re[0]=true;
queue.offer(d.next);
}
}
}
return reSet;
}
}
}