非确定有限状态自动机
当且仅当一个NFA从状态0开始从头读去了一段文本中的所有字符,进行了一系列状态转换,并最终到达了接受状态时,则称该NFA识别了一个文本字符串。
5 模拟NFA的运行
5.1 自动机的表示
用char
数组re[]
表示正则表达式本身,也表达了匹配转换。使用有向图
G
表示所有不读取字符的转换(
5.2 NFA的模拟与可达性
纪录自动机在检查当前输入字符时可能遇到的所有状态的集合。关键点在于解决多点可达性的问题。查找所有从状态
0
出发经过
Proposition Q. 判定一个长度为 M 的正则表达式所对应的NFA能否识别一段长度为
N 的文本所需的时间在最坏情况下和 MN 成正比。
下面这段代码即为NFA的简单构造和匹配过程,还有一些正则表达式的功能需要补全。
package algs4.strings.re;
import java.util.Stack;
import edu.princeton.cs.algs4.Bag;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.DirectedDFS;
import edu.princeton.cs.introcs.*;
class NFA {
private int M;
private Digraph G;
private char[] re;
public NFA(String regexp){
re =regexp.toCharArray();
M = re.length;
G = new Digraph(M + 1);
Stack<Integer> stk = new Stack<Integer>();
for( int i = 0; i < M; i++){
int lp = i;
if (re[i] == '(' || re[i] == '|')
stk.push(i);
else if (re[i] == ')'){
int or = stk.pop();
if(or == '|'){//Suppose there's only one '|' in a parentheses
lp = stk.pop();
G.addEdge(lp, or + 1);
G.addEdge(or, i);
}
else lp = or;
}
if (i < M - 1 && re[i + 1] == '*'){
G.addEdge(lp,i+1);
G.addEdge(i+1, lp);
}
if (re[i] == '(' || re[i] ==')' || re[i] == '*')
G.addEdge(i, i+1);
}
}
public boolean recognies(String txt){
Bag<Integer> bag = new Bag<Integer>();
DirectedDFS dfs = new DirectedDFS(G, 0);
for(int i = 0; i < G.V(); i++){
if(dfs.marked(i) ) bag.add(i);
}
for(int i = 0; i < txt.length(); i++){
Bag<Integer> match = new Bag<Integer>();
for(int v: bag)
if(v < M)
if(re[v] == txt.charAt(i) || re[v] == '.')
match.add(v + 1);
bag = new Bag<Integer>();
dfs = new DirectedDFS(G,match);
for( int v = 0; v < G.V(); v ++){
if(dfs.marked(v)) bag.add(v);
}
for(int v: bag) if(v == M) return true;
}
return false;
}
public static void main(String[] args) {
String txt = StdIn.readString();
String re = StdIn.readString();
NFA nfa = new NFA(re);
StdOut.println(nfa.recognies(txt));
}
}